How to get Outlook Property Names using extended MAPI GetNamesFromIDs

82 Views Asked by At

I am trying to view property names from an Outlook message using extended MAPI, ie the contents of a MAPINAMEID structure.

I get a list of property tags for a message and then I am passing the below code a single Tag (uTag) which has a value greater than 0x80000000.

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

The snippet of relevant code from something much larger.

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);

The structures look like this

[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. Is my PtrToStructure for MAPINAMEID correct? I only give 1 tag in the SPropTagArray and pNames always equals 1.
  2. Is IntPtr pGuid actually a Guid or do I need a special structure for this? The results it produces cannot be found on the message.
  3. Is it possible that ulKind returns a number other than 0 or 1? (Assuming that is what it should return for MNID_STRING (1) and MNID_ID (2)
  4. Marshal.PtrToStringUni(nameID.Kind.lpszNameW) also is raising a Attempted to read or write protected memory. This is often an indication that other memory is corrupt. after a few calls.

Initially I was not even looking at the MAPINAMEID . I was simply passing the IntPtr propNames without touching it back into GetIDsFromNames with a CREATE flag to add the property to a new message. I noticed that some calls to GetIDsFromNames was failing with an Invalid args as the result. I assume this is because of something in the MAPINAMEID.

EDIT 1 - Based on Dmitry's comments As he wanted a little more context, this code is part of a VSTO Outlook addin which takes the MAPIOBJECT from OOM and then copies each individual property. Part of that copying includes the NamedProperties, however, for certain reasons I want to check a few that are added and remove them, thus the need to know the names.

I am now using the following, the first 2 arguments are by ref I am passing null for lpPropSetGuid

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

I did try to use an unit[] for lppPropTags as I think was suggested but got an invalid args response.

This appears to work.

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);

The most major changes I made were to MAPINAMEID

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

Documentation seems to say that I get back ONLY a GUID struct so I changed over to one

[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;
}

I also slightly altered how I get the MAPINAMEID (currently hard coded for 1 element only)

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]);

Currently no errors but not sure about cleaning up memory yet.

Dmitry my understanding (yet again from you) is that when I add a named property to a new message with CREATE in GetIDsFromNames that a new tag number is created. However from what i can tell is it possible that the same tag is generated when just copying these properties?

0

There are 0 best solutions below