Hacking Outlook Express - COM Interop
Written by Mike James   
Thursday, 23 July 2009
Article Index
Hacking Outlook Express - COM Interop
Creating a COM object
Exposing COM
OE Analysis
Mail analysis

 

OE Analysis

As a better example of doing something useful let’s use the OE COM API to create a tree display of how many emails are in each folder. You can extend this to including information about size, age and other message statistics – the same technique applies.

To access all of the subfolders we need three more Interface functions. Unfortunately these lie at the end of the vtable. However, as we aren’t using the intervening functions we can just define placeholder functions and fill them in later:

void GetFolderProps();
void CopyMoveMessages();
void RegisterNotification();
void UnregisterNotification();
void CompactAll();

Full definitions, including parameters and attributes are given in the source code in the  CodeBin.

The three functions that we do want to use are:

[PreserveSig]
Int32 GetFirstSubFolder(
[In, MarshalAs(UnmanagedType.U4)]
Int32 dwFolderId,
[Out, MarshalAs(UnmanagedType.Struct)]
out _FOLDERPROPS pProps,
[Out, MarshalAs(UnmanagedType.U4)]
out Int32 phEnum);
[PreserveSig]
Int32 GetNextSubFolder(
[In, MarshalAs(UnmanagedType.U4)]
Int32 hEnum,
[Out, MarshalAs(UnmanagedType.Struct)]
out _FOLDERPROPS pProps);
void GetSubFolderClose(
[In, MarshalAs(UnmanagedType.U4)]
Int32 hEnum);
}

The GetFirstSubFolder function returns a handle to a folder enumeration as well as a structure giving information about the first sub-folder. After you have the first sub-folder you can then call GetNextSubFolder using the same enumeration handle until all the sub-folders have been processed.Finally calling GetSubFolderClose releases the enumeration handle.

A new feature with these function definitions is the use of [PreserveSig]. A COM function usually returns an error code called HRESULT. COM interop knows enough about the way things work to intercept this result and convert it into a .NET exception if there is an error.

Hence all of the functions we have defined earlier haven’t returned an error code. This is usually how you want things arranged but occasionally a COM function will return status information in HRESULT that you want to process.

GetFirstSubFolder and GetNextSubFolder return an HRESULT of 0 if they have retrieved a sub-folder and 1if there are no more sub-folders. To process this result we have to add the [PreserveSig] attribute and define the functions as returning an Int32.

To make use of these new functions we also need a struct, again defined in the header file, to hold the folder properties:

[StructLayout(LayoutKind.Sequential, 
Pack = 1,
CharSet = CharSet.Ansi)]
internal unsafe struct _FOLDERPROPS
{
internal Int32 cbSize;
internal Int32 dwFolderId;
internal Int32 cSubFolders;
internal Int32 sfType;
internal Int32 cUnread;
internal Int32 cMessage;
internal fixed byte szName[256];
};

As this needs a fixed 256-byte buffer to hold the folder name, it has to be declared as unsafe and you have to set the “allow unsafe” condition in the project properties.

Using a fixed size buffer isn’t that unsafe but it is still better to convert it into a 100% standard struct before passing it back to the rest of the application. So we define a second, almost identical, struct and a function, which copies the unsafe struct into the type safe struct:

public struct FOLDERPROPS
{
public Int32 cbSize;
public Int32 dwFolderId;
public Int32 cSubFolders;
public Int32 sfType;
public Int32 cUnread;
public Int32 cMessage;
public Int32 hEnum;
public string szName;
} ;
private FOLDERPROPS transfer(
_FOLDERPROPS FP)
{
FOLDERPROPS fp = new FOLDERPROPS();
fp.cbSize = FP.cbSize;
fp.cMessage = FP.cMessage;
fp.cSubFolders = FP.cSubFolders;
fp.cUnread = FP.cUnread;
fp.dwFolderId = FP.dwFolderId;
fp.hEnum = 0;
fp.sfType = FP.sfType;
unsafe
{
fp.szName = Marshal.PtrToStringAnsi(
(IntPtr)FP.szName);
}
return fp;
}

Notice that the safe struct is public so calling programs can make use of it, but the unsafe struct is internal and not accessible to the outside world.

At this point we could wrap the three new functions in three methods, but why not do the job better than the API does?

It is fairly easy to write a single method that combines getting the first and subsequent folders and automatically closes the enumeration handle when there are no more sub-folders. The idea is that if the enumeration handle passed to the method is 0 then we attempt to get the first sub-folder of the folder specified by the FolderId. If the enumeration handle is already set then we already have the first sub-folder and we attempt to get the next folder.

Finally if any attempt to get a folder fails we close the enumeration handle. This gives us a single method that just gets the next available sub-folder:

public FOLDERPROPS GetNextSubFolder(
Int32 Id,Int32 hEnum)
{
_FOLDERPROPS FP = new _FOLDERPROPS();
FP.cbSize = Marshal.SizeOf(FP);
Int32 result;
if (hEnum == 0)
{
result = OEFolders.GetFirstSubFolder(
Id, out FP, out hEnum);
}
else
{
result = OEFolders.GetNextSubFolder(
hEnum, out FP);
}
FOLDERPROPS fp = transfer(FP);
if (result != 0)
{
if(hEnum!=0)
OEFolders.GetSubFolderClose(hEnum);
hEnum = 0;
}
fp.hEnum = hEnum;
return fp;
}

The returned hEnum field will be 0 if there are no more sub-folders. It is always worth remembering that you don’t have to slavishly follow the implementation used by the COM programmer. If you can package the same functionality in a way that is easier to use – do it!

<ASIN:0764574817>

<ASIN:0735621632>

<ASIN:0321545613>



Last Updated ( Wednesday, 16 September 2009 )