如何使用扩展 MAPI GetNamesFromID 获取 Outlook 属性名称

How to get Outlook Property Names using extended MAPI GetNamesFromIDs

提问人:darbid 提问时间:9/28/2023 最后编辑:darbid 更新时间:9/29/2023 访问量:61

问:

我正在尝试使用扩展 MAPI(即 MAPINAMEID 结构的内容)查看 Outlook 邮件中的属性名称。

我得到一个消息的属性标签列表,然后我向下面的代码传递一个值大于0x80000000的标签(uTag)。

GetNamesFromIDs(out IntPtr lppPropTags, ref Guid lpPropSetGuid, uint ulFlags,
                out uint lpcPropNames, out IntPtr lpppPropNames);

来自更大内容的相关代码片段。

IntPtr propNames;
SPropTagArray ptArray = new SPropTagArray
{
    cValues = (uint)1,
    aulPropTag = new uint[] { uTag },
};
IntPtr propTagArrayPtr = Marshal.AllocHGlobal(Marshal.SizeOf<SPropTagArray>());
Marshal.StructureToPtr(ptArray, propTagArrayPtr, true);

HRESULT hr = mapiObj_.GetNamesFromIDs(out propTagArrayPtr, Guid.Empty, 0, out uint pNames, out propNames);

if (hr == HRESULT.S_OK && propNames != IntPtr.Zero)
{
    MAPINAMEID nameID = (MAPINAMEID)Marshal.PtrToStructure(propNames, typeof(MAPINAMEID));
    LPGuid = (Guid)Marshal.PtrToStructure(nameID.pGuid, typeof(Guid));
    ULKind = (uint)nameID.ulKind;
    PropKind = nameID.Kind;

    if ((KindTypes)ULKind == KindTypes.MNID_ID)
    {
        IID = nameID.Kind.lID;
    }
    else if ((KindTypes)ULKind == KindTypes.MNID_STRING)
    {
        Name = Marshal.PtrToStringUni(nameID.Kind.lpszNameW);
    }
}
MAPINative.MAPIFreeBuffer(propTagArrayPtr);
MAPINative.MAPIFreeBuffer(propNames);

结构如下所示

[StructLayout(LayoutKind.Sequential)]
public struct SPropTagArray
{
    public uint cValues;
    public uint[] aulPropTag;
}

[StructLayout(LayoutKind.Sequential)]
    struct MAPINAMEID
    {
        public IntPtr pGuid;
        public int ulKind;
        public Kind Kind;
    }

[StructLayout(LayoutKind.Explicit)]
    struct Kind
    {
        [FieldOffset(0)]
        public int lID;
        [FieldOffset(0)]
        public IntPtr lpszNameW;
    }
  1. MAPINAMEID 的 PtrToStructure 是否正确?我在 SPropTagArray 中只给出 1 个标签,而 pNames 总是等于 1。
  2. IntPtr pGuid 实际上是一个 Guid 还是我需要一个特殊的结构?在邮件中找不到它产生的结果。
  3. ulKind 是否有可能返回 0 或 1 以外的数字?(假设这是MNID_STRING (1) 和 MNID_ID (2) 应该返回的内容)
  4. Marshal.PtrToStringUni(nameID.Kind.lpszNameW) 还引发了尝试读取或写入受保护的内存。这通常表示其他内存已损坏。打了几个电话后。

最初我什至没有看 MAPINAMEID 。我只是传递了 IntPtr propNames,而没有使用 CREATE 标志将其重新触及 GetIDsFromNames 以将属性添加到新消息中。我注意到对 GetIDsFromNames 的一些调用失败,结果显示参数无效。我认为这是因为 MAPINAMEID 中的某些内容。

编辑 1 - 基于德米特里的评论由于他需要更多的上下文,因此此代码是 VSTO Outlook 外接程序的一部分,该外接程序从 OOM 中获取 MAPIOBJECT,然后复制每个单独的属性。复制的一部分包括 NamedProperties,但是,出于某些原因,我想检查一些添加并删除它们,因此需要知道名称。

我现在使用以下内容,前 2 个参数是 ref 我为 lpPropSetGuid 传递 null

HRESULT GetNamesFromIDs(ref IntPtr lppPropTags, ref IntPtr lpPropSetGuid, uint ulFlags,out uint lpcPropNames, out IntPtr lpppPropNames);

我确实尝试为lppPropTags使用unit[],但我认为有人建议,但得到了无效的args响应。

这似乎有效。

SPropTagArray propTagArray = new SPropTagArray
{
    cValues = 1,
    aulPropTag = new uint[1]
};
propTagArray.aulPropTag[0] = uTag; 
IntPtr propTagArrayPtr = Marshal.AllocHGlobal(Marshal.SizeOf(propTagArray));
Marshal.StructureToPtr(propTagArray, propTagArrayPtr, false);

我所做的最重大的更改是 MAPINAMEID

[StructLayout(LayoutKind.Sequential)]
private struct MAPINAMEID_A
{
    public IntPtr lpguid;
    public uint ulKind;
    public IntPtr lpwstrName; // or lID
};

文档似乎说我只得到一个 GUID 结构,所以我换了一个

[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct _GUID
{
    public Int32 Data1;
    public Int16 Data2;
    public Int16 Data3;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public byte[] Data4;
}

我还略微改变了获取 MAPINAMEID 的方式(目前仅针对 1 个元素进行硬编码)

MAPINAMEID_A nameID = new MAPINAMEID_A();
for (int i = 0; i < 1; i++)
{
    // Get the pointer at the current index  
    IntPtr ptrElement = Marshal.ReadIntPtr(mAPINAMEIDArray, i * IntPtr.Size);

    // Marshal the pointer to a MAPINAMEID structure  
    nameID = Marshal.PtrToStructure<MAPINAMEID_A>(ptrElement);
}
_GUID DLPGuid = (_GUID)Marshal.PtrToStructure(nameID.lpguid, typeof(_GUID));

LPGuid = new Guid(DLPGuid.Data1, DLPGuid.Data2, DLPGuid.Data3,
    DLPGuid.Data4[0], DLPGuid.Data4[1], DLPGuid.Data4[2], DLPGuid.Data4[3],
    DLPGuid.Data4[4], DLPGuid.Data4[5], DLPGuid.Data4[6], DLPGuid.Data4[7]);

目前没有错误,但不确定是否清理内存。

德米特里,我的理解(再次来自你)是,当我在 GetIDsFromNames 中使用 CREATE 将命名属性添加到新消息时,会创建一个新的标记号。但是,据我所知,在复制这些属性时是否有可能生成相同的标签?

Outlook VSTO 调用 MAPI

评论

0赞 Dmitry Streblechenko 9/29/2023
中的第一个参数是 ,而不是 。第二个参数是指向 GUID 的指针 - pass ,而不是 .你到底想达到什么目的?如果使用 Redemption(我是它的作者)是一个选项,它会在其所有包装器(/等)上公开方法。GetNamesFromIDsrefoutnullGUID.EmptyGetNamesFromIDsIMAPIPropRDOStore / RDOFolder / RDOMail / RDOAttachment
0赞 darbid 9/29/2023
我在尝试什么?复制在 Outlook 中显示为加密的 MAPI 对象的每个属性。阅读您 20 年的评论/答案,这是唯一的方法。当我在使用 p/invoke 失败时,我会使用 Redemption。我有几个问题,有一天我会在一个新问题中发布。感谢您的提示,它们没有太大变化,我的感觉是我的 MAPINAMEID 处理是错误的。
0赞 Dmitry Streblechenko 9/29/2023
这些定义对我来说看起来还可以,但我从未尝试过用 C# 调用......GetNamesFromIDs
0赞 Dmitry Streblechenko 9/29/2023
此外,尝试使用单个元素数组定义 SPropTagArray。或者只是一个 int 值。
1赞 Dmitry Streblechenko 9/29/2023
这是正确的。不清楚这是否在加载项中,我将 vsto 添加到标签中。

答: 暂无答案