|
I think you can try to use another interface - IMessageStore. You can initialize it using the absolute path (in terms of file system) to the folder which contains the .dbx files. For example (on my machine) it is "C:\Documents and Settings\Alexey\Local Settings\Application Data\Identities\{<some_guid>}\Microsoft\Outlook Express". After proper initialization you will have access to whole tree of OE's folders located in this file folder.
You can get the instance using simple call of CoCreateInstance:
IMessageStore* p_store = NULL;
...
HRESULT hr = ::CoCreateInstance(CLSID_MessageStore,NULL,CLSCTX_INPROC_SERVER,IID_IMessageStore,(void**)&store );
if (FAILED(hr) || (p_store == NULL)) return E_FAIL;
hr = p_store->Initialize(sz_path_to_dbx_folder);
...
Then you can use member functions:
EnumChildren - to enumerate folders,
OpenFolder - to open folder,
OpenSpecialFolder... and so on.
They works very like the ones from IStoreNamespace (actually these members of IStoreNamespace delegate their calls to IMessageStore). If you see, for example, OpenFolder (in difference with such member from IStoreNamespace) returns the pointer to IMessageFolder, instead of IStoreFolder - and also wait for parameter IMessageServer.
Actually you can use IMessageFolder as more general version of IStoreFolder.
hm... I think I can summ the knowledge of internal OE structures into standalone article here on CodeProject, how you think? :->
Below is the necessary declarations (they are non-official - so, it may contain misstypes; be careful!)
<br />
DEFINE_GUID(CLSID_MessageStore, 0x101A8FB9, 0xF1B9, 0x11d1, 0x9A, 0x56, 0x0, 0xC0, 0x4F, 0xA3, 0x09, 0xD4);<br />
<br />
DEFINE_GUID(IID_IMessageStore, 0xE883FC75, 0xEC08, 0x11D1, 0x9A, 0x53, 0x0, 0xC0, 0x4F, 0xA3, 0x09, 0xD4);<br />
<br />
struct tagMESSAGEINFO;<br />
typedef tagMESSAGEINFO MESSAGEINFO, __RPC_FAR *LPMESSAGEINFO;<br />
<br />
struct tagFINDINFO;<br />
struct tagGETCOUNTTYPE;<br />
struct tagAPPLYCHILDRENTYPE;<br />
struct tagMARK_TYPE;<br />
struct tagFOLDERSORTINFO;<br />
struct tagGETNEXTTYPE;<br />
struct tagROWMESSAGETYPE;<br />
struct tagRELATIVEROWTYPE;<br />
struct tagFINDNEXTFLAGS;<br />
struct tagFOLDER_OPTIONS;<br />
<br />
interface IListSelector;<br />
interface IMessageServer;<br />
interface IMessageTableNotify;<br />
<br />
typedef DWORD MESSAGEID__;<br />
typedef unsigned long FOLDERID__;<br />
typedef DWORD tagADJUSTFLAGS;<br />
<br />
interface IStoreCallback : public IUnknown<br />
{<br />
};<br />
<br />
struct HLOCK__;<br />
struct tagTABLEINDEX;<br />
struct tagRESULTLIST;<br />
struct tagFOLDERINFO;<br />
struct HROWSET__;<br />
struct HTRANSACTION__;<br />
typedef unsigned short ushort;<br />
typedef unsigned char uchar;<br />
<br />
interface IDatabaseNotify;<br />
interface IDatabaseProgress;<br />
interface IDatabase;<br />
interface IEnumerateFolders;<br />
interface IImnAccount;<br />
<br />
interface IMessageFolder;<br />
<br />
interface IMessageStore : public IUnknown<br />
{<br />
virtual HRESULT STDMETHODCALLTYPE Lock(HLOCK__ * *);<br />
virtual HRESULT STDMETHODCALLTYPE Unlock(HLOCK__ * *);<br />
virtual HRESULT STDMETHODCALLTYPE InsertRecord(void *);<br />
virtual HRESULT STDMETHODCALLTYPE UpdateRecord(void *);<br />
virtual HRESULT STDMETHODCALLTYPE DeleteRecord(void *);<br />
virtual HRESULT STDMETHODCALLTYPE FindRecord(unsigned long,unsigned long,void *,unsigned long *);<br />
virtual HRESULT STDMETHODCALLTYPE GetRowOrdinal(unsigned long,void *,unsigned long *);<br />
virtual HRESULT STDMETHODCALLTYPE FreeRecord(void *);<br />
virtual HRESULT STDMETHODCALLTYPE GetUserData(void *,unsigned long);<br />
virtual HRESULT STDMETHODCALLTYPE SetUserData(void *,unsigned long);<br />
virtual HRESULT STDMETHODCALLTYPE GetRecordCount(unsigned long,unsigned long *);<br />
virtual HRESULT STDMETHODCALLTYPE GetIndexInfo(unsigned long,char * *,struct tagTABLEINDEX *);<br />
virtual HRESULT STDMETHODCALLTYPE ModifyIndex(unsigned long,char const *,struct tagTABLEINDEX const *);<br />
virtual HRESULT STDMETHODCALLTYPE DeleteIndex(unsigned long);<br />
virtual HRESULT STDMETHODCALLTYPE CreateRowset(unsigned long,unsigned long,struct HROWSET__ * *);<br />
virtual HRESULT STDMETHODCALLTYPE SeekRowset(struct HROWSET__ *,enum tagSEEKROWSETTYPE,long,unsigned long *);<br />
virtual HRESULT STDMETHODCALLTYPE QueryRowset(struct HROWSET__ *,long,void * *,unsigned long *);<br />
virtual HRESULT STDMETHODCALLTYPE CloseRowset(struct HROWSET__ * *);<br />
virtual HRESULT STDMETHODCALLTYPE CreateStream(unsigned long *);<br />
virtual HRESULT STDMETHODCALLTYPE DeleteStream(unsigned long);<br />
virtual HRESULT STDMETHODCALLTYPE CopyStream(struct IDatabase *,unsigned long,unsigned long *);<br />
virtual HRESULT STDMETHODCALLTYPE OpenStream(enum tagACCESSTYPE,unsigned long,struct IStream * *);<br />
virtual HRESULT STDMETHODCALLTYPE ChangeStreamLock(struct IStream *,enum tagACCESSTYPE);<br />
virtual HRESULT STDMETHODCALLTYPE RegisterNotify(unsigned long,unsigned long,unsigned long,IDatabaseNotify *);<br />
virtual HRESULT STDMETHODCALLTYPE DispatchNotify(struct IDatabaseNotify *);<br />
virtual HRESULT STDMETHODCALLTYPE SuspendNotify(struct IDatabaseNotify *);<br />
virtual HRESULT STDMETHODCALLTYPE ResumeNotify(struct IDatabaseNotify *);<br />
virtual HRESULT STDMETHODCALLTYPE UnregisterNotify(struct IDatabaseNotify *);<br />
virtual HRESULT STDMETHODCALLTYPE LockNotify(unsigned long,struct HLOCK__ * *);<br />
virtual HRESULT STDMETHODCALLTYPE UnlockNotify(struct HLOCK__ * *);<br />
virtual HRESULT STDMETHODCALLTYPE GetTransaction(struct HTRANSACTION__ * *,enum tagTRANSACTIONTYPE *,void *,void *,unsigned long *,struct tagORDINALLIST *);<br />
virtual HRESULT STDMETHODCALLTYPE MoveFileA(unsigned short const *);<br />
virtual HRESULT STDMETHODCALLTYPE SetSize(unsigned long);<br />
virtual HRESULT STDMETHODCALLTYPE Repair(void);<br />
virtual HRESULT STDMETHODCALLTYPE Compact(struct IDatabaseProgress *,unsigned long);<br />
virtual HRESULT STDMETHODCALLTYPE HeapAllocate(unsigned long,unsigned long,void * *);<br />
virtual HRESULT STDMETHODCALLTYPE HeapFree(void *);<br />
virtual HRESULT STDMETHODCALLTYPE GenerateId(unsigned long *);<br />
virtual HRESULT STDMETHODCALLTYPE GetClientCount(unsigned long *);<br />
virtual HRESULT STDMETHODCALLTYPE GetFile(unsigned short * *);<br />
virtual HRESULT STDMETHODCALLTYPE GetSize(unsigned long *,unsigned long *,unsigned long *,unsigned long *);<br />
virtual HRESULT STDMETHODCALLTYPE Initialize(LPCSTR pszPath);<br />
virtual HRESULT STDMETHODCALLTYPE Validate(unsigned long);<br />
virtual HRESULT STDMETHODCALLTYPE GetDirectory(int,LPSTR lpString1,int iMaxLength);<br />
virtual HRESULT STDMETHODCALLTYPE Synchronize(FOLDERID__ *,unsigned long,struct IStoreCallback *);<br />
virtual HRESULT STDMETHODCALLTYPE FindServerId(char const *,FOLDERID__ * *);<br />
virtual HRESULT STDMETHODCALLTYPE CreateServer(IImnAccount *,ULONG,FOLDERID__ * *);<br />
virtual HRESULT STDMETHODCALLTYPE CreateFolder(ULONG,tagFOLDERINFO *,IStoreCallback *);<br />
virtual HRESULT STDMETHODCALLTYPE OpenSpecialFolder(FOLDERID__ *,IMessageServer *,uchar,IMessageFolder**);<br />
virtual HRESULT STDMETHODCALLTYPE OpenFolder(FOLDERID__ *,IMessageServer *,ULONG,IMessageFolder**);<br />
virtual HRESULT STDMETHODCALLTYPE MoveFolder(FOLDERID__ *,FOLDERID__ *,ULONG,IStoreCallback *);<br />
virtual HRESULT STDMETHODCALLTYPE RenameFolder(FOLDERID__ *,char const *,ULONG,IStoreCallback *);<br />
virtual HRESULT STDMETHODCALLTYPE DeleteFolder(FOLDERID__ *,ULONG,IStoreCallback *);<br />
virtual HRESULT STDMETHODCALLTYPE GetFolderInfo(FOLDERID__ *,tagFOLDERINFO *);<br />
virtual HRESULT STDMETHODCALLTYPE GetSpecialFolderInfo(FOLDERID__ *,uchar,tagFOLDERINFO *);<br />
virtual HRESULT STDMETHODCALLTYPE SubscribeToFolder(FOLDERID__ *,int,IStoreCallback *);<br />
virtual HRESULT STDMETHODCALLTYPE HrSetNoSecUICallback(ULONG,long (*)(ULONG));<br />
virtual HRESULT STDMETHODCALLTYPE UpdateFolderCounts(FOLDERID__ *,long,long,long,long);<br />
virtual HRESULT STDMETHODCALLTYPE EnumChildren(FOLDERID__ *,int,IEnumerateFolders **);<br />
virtual HRESULT STDMETHODCALLTYPE GetAdvise(ULONG *,ULONG *,IAdviseSink * *);<br />
};<br />
<br />
interface IMessageFolder : public IUnknown<br />
{<br />
virtual HRESULT STDMETHODCALLTYPE Lock(HLOCK__ * *);<br />
virtual HRESULT STDMETHODCALLTYPE Unlock(HLOCK__ * *);<br />
virtual HRESULT STDMETHODCALLTYPE InsertRecord(void *);<br />
virtual HRESULT STDMETHODCALLTYPE UpdateRecord(void *);<br />
virtual HRESULT STDMETHODCALLTYPE DeleteRecord(void *);<br />
virtual HRESULT STDMETHODCALLTYPE FindRecord(ULONG,ULONG,void *,ULONG *);<br />
virtual HRESULT STDMETHODCALLTYPE GetRowOrdinal(ULONG,void *,ULONG *);<br />
virtual HRESULT STDMETHODCALLTYPE FreeRecord(void *);<br />
virtual HRESULT STDMETHODCALLTYPE GetUserData(void *,ULONG);<br />
virtual HRESULT STDMETHODCALLTYPE SetUserData(void *,ULONG);<br />
virtual HRESULT STDMETHODCALLTYPE GetRecordCount(ULONG,ULONG *);<br />
virtual HRESULT STDMETHODCALLTYPE GetIndexInfo(ULONG,char * *,tagTABLEINDEX *);<br />
virtual HRESULT STDMETHODCALLTYPE ModifyIndex(ULONG,char const *,tagTABLEINDEX const *);<br />
virtual HRESULT STDMETHODCALLTYPE DeleteIndex(ULONG);<br />
virtual HRESULT STDMETHODCALLTYPE CreateRowset(ULONG,ULONG,HROWSET__ * *);<br />
virtual HRESULT STDMETHODCALLTYPE SeekRowset(HROWSET__ *,tagSEEKROWSETTYPE,long,ULONG *);<br />
virtual HRESULT STDMETHODCALLTYPE QueryRowset(HROWSET__ *,long,void * *,ULONG *);<br />
virtual HRESULT STDMETHODCALLTYPE CloseRowset(HROWSET__ * *);<br />
virtual HRESULT STDMETHODCALLTYPE CreateStream(ULONG *);<br />
virtual HRESULT STDMETHODCALLTYPE DeleteStream(ULONG);<br />
virtual HRESULT STDMETHODCALLTYPE CopyStream(IDatabase *,ULONG,ULONG *);<br />
virtual HRESULT STDMETHODCALLTYPE OpenStream(tagACCESSTYPE,ULONG,IStream * *);<br />
virtual HRESULT STDMETHODCALLTYPE ChangeStreamLock(IStream *,tagACCESSTYPE);<br />
virtual HRESULT STDMETHODCALLTYPE RegisterNotify(ULONG,ULONG,ULONG,IDatabaseNotify *);<br />
virtual HRESULT STDMETHODCALLTYPE DispatchNotify(IDatabaseNotify *);<br />
virtual HRESULT STDMETHODCALLTYPE SuspendNotify(IDatabaseNotify *);<br />
virtual HRESULT STDMETHODCALLTYPE ResumeNotify(IDatabaseNotify *);<br />
virtual HRESULT STDMETHODCALLTYPE UnregisterNotify(IDatabaseNotify *);<br />
virtual HRESULT STDMETHODCALLTYPE LockNotify(ULONG,HLOCK__ * *);<br />
virtual HRESULT STDMETHODCALLTYPE UnlockNotify(HLOCK__ * *);<br />
virtual HRESULT STDMETHODCALLTYPE GetTransaction(HTRANSACTION__ * *,tagTRANSACTIONTYPE *,void *,void *,ULONG *,tagORDINALLIST *);<br />
virtual HRESULT STDMETHODCALLTYPE MoveFileA(ushort const *);<br />
virtual HRESULT STDMETHODCALLTYPE SetSize(ULONG);<br />
virtual HRESULT STDMETHODCALLTYPE Repair(void);<br />
virtual HRESULT STDMETHODCALLTYPE Compact(IDatabaseProgress *,ULONG);<br />
virtual HRESULT STDMETHODCALLTYPE HeapAllocate(ULONG,ULONG,void * *);<br />
virtual HRESULT STDMETHODCALLTYPE HeapFree(void *);<br />
virtual HRESULT STDMETHODCALLTYPE GenerateId(ULONG *);<br />
virtual HRESULT STDMETHODCALLTYPE GetClientCount(ULONG *);<br />
virtual HRESULT STDMETHODCALLTYPE GetFile(ushort * *);<br />
virtual HRESULT STDMETHODCALLTYPE GetSize(ULONG *,ULONG *,ULONG *,ULONG *);<br />
virtual HRESULT STDMETHODCALLTYPE Initialize(IMessageStore *,IMessageServer *,ULONG,FOLDERID__ *);<br />
virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(int);<br />
virtual HRESULT STDMETHODCALLTYPE UpdateRegistry(int);<br />
virtual HRESULT STDMETHODCALLTYPE GetFolderId(FOLDERID__ * *);<br />
virtual HRESULT STDMETHODCALLTYPE GetMessageFolderId(MESSAGEID__ *,FOLDERID__ * *);<br />
virtual HRESULT STDMETHODCALLTYPE GetAdvise(ULONG *,ULONG *,IAdviseSink * *);<br />
virtual HRESULT STDMETHODCALLTYPE OpenMessage(MESSAGEID__ *,ULONG,IMimeMessage * *,IStoreCallback *);<br />
virtual HRESULT STDMETHODCALLTYPE SaveMessage(MESSAGEID__ * *,ULONG,ULONG,IStream *,IMimeMessage *,IStoreCallback *);<br />
virtual HRESULT STDMETHODCALLTYPE HrSetNoSecUICallback(ULONG,long (*)(ULONG));<br />
virtual HRESULT STDMETHODCALLTYPE SetMessageFlags(tagMESSAGEIDLIST *,tagADJUSTFLAGS *,tagRESULTLIST *,IStoreCallback *);<br />
virtual HRESULT STDMETHODCALLTYPE CopyMessages(IMessageFolder *,ULONG,tagMESSAGEIDLIST *,tagADJUSTFLAGS *,tagRESULTLIST *,IStoreCallback *);<br />
virtual HRESULT STDMETHODCALLTYPE DeleteMessages(ULONG,tagMESSAGEIDLIST *,tagRESULTLIST *,IStoreCallback *);<br />
virtual HRESULT STDMETHODCALLTYPE ResetFolderCounts(ULONG,ULONG,ULONG,ULONG);<br />
virtual HRESULT STDMETHODCALLTYPE IsWatched(char const *,char const *);<br />
virtual HRESULT STDMETHODCALLTYPE GetDatabase(IDatabase * *);<br />
virtual HRESULT STDMETHODCALLTYPE ConnectionRelease(void);<br />
virtual HRESULT STDMETHODCALLTYPE ConnectionRelease1(void);<br />
virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp1(int);<br />
};<br />
|
|
|
|
|
Hi,
Thanks for given declaration, it very helpful
for me.
Have you any idea about clsid and IID for IMessageList
in Windows Mail for Windows Vista?
|
|
|
|
|
Hi Pablo, great article
I want to extract the attachments from the e-mails and for this I'm using IMimeBody interface. Until here no problem I was able to extract them successfully.
The problems occurs when I'm using the UNICODE version of my program. I'm not able to get the filenames correctly. Do I have to use the UNICODE version of the interface IMimeBody - IMimeBodyW? If so do you in which header is declared? It seems that is not in the mimeole.h. Or do I have to find a newer version of this header.
Thanks,
|
|
|
|
|
Sorry, I am not Pablo...
Which filenames you are not able to get correctly? I didn't find any filenames in methods of IMimeBody, except the one in "SaveToFile" method.
I don't think that any IMimeBodyW exists - since this is COM-interface, not the WIN API function which can be substituted with different acronyms conditionally from the UNICODE macro definition.
Here you have only one IMimeBody with it's own IID. And it gives you the data encoded according different RFC docs, not according your current unicode settings. So, I think you just ought to use such things as MultiByteToWideChar (or OLE macro for it - a2w)
|
|
|
|
|
Hi there,
thanks for your reply
Here is the link for description of IMimeBodyW
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/outlookexpress/oe/reference/ifaces/imimebodyw/imimebodyw.asp
It has method that is getting the name of the body, in this case the attachment -
LPSTR pString;
pMimeBody->GetDisplayName( &pString );
The thing is the parameter of GetDisplayName is a pointer to LPSTR and I need a LPWSTR. Anyway I will try with A2W to see if the chars will be converted
Thanks
|
|
|
|
|
This is a great article.
Can anybody give me an idea to get the selected message Id to get all the information for the e-mail selected in Inbox or any other folder selected.
Thanks in advance.
Advaitha P
|
|
|
|
|
The interaction with user is totally another issue - and I think this is the very reason why Pablo's OEAPI exists.
Internally OE doesn't use IStoreNamespace and IStoreFolder, but instead it uses IMessageFolder and IMessageTable. The last one represents the view for list of messages you currently see on the screen - and using it's method GetSelectedRows you can get the list of selected/focused messages. All works perfectly with one exception - the instance of IMessageTable which contains information you need is own by OE itself, and you can't access it easy way from the outside.
The most popular method to get such info is so-called "mirroring". You must instantiate the copy of IMessageFolder (you can do it using CoCreateInstance with CLSID {233A9692-667E-11D1-9DFB-006097D50408} and IID {DE89B950-A597-11D1-9E46-006097D50408}) - then get from it the copy of IMessageTable. Then you must get from the registry the sort order of columns which OE itself uses to display the list - and finally you must hook all events which can change this sort order (for example, clicking on headers of listview and etc.). By this you will have your own copy of IMessageTable sinchronyzed with one which OE uses in this very moment - and by hooking selected elements from displayed messagelist you can actually get the MessageIDs corresponded to them.
This method is very stupid and require a mass of low-level coding, but it works.
The declarations of IMessageFolder and IMessageTable interfaces you can get from msoe.dll using debugging symbols from MS, and debugger - just search for "vftable" for IMessageFolder and IMessageTable and write your own header file.
|
|
|
|
|
On the way of continuing my previous post.
As you can imagine, the main problem of accessing current message list (selection, focuses) is isolation of the instance of IMessageTable which contains the data we needs.
Though the "mirroring" method of tracking this data is working, it is not so plain and easy in realization because of the mass of coding.
Another way - and I believe, it is much better for this purpose - is interface hooking.
Suppose that your application is already placed in the address space of OE. The OE itself is located in the library msoe.dll (msimn.exe is actually only loader of msoe). So, if you instantiate from your application any class which is realized in msoe, the COM will not load the second copy of msoe into the memory, but simple redirect your call to the existing instance.
And now - see how it works:
1. Instantiate IMessageFolder (using CoCreateInstance).
2. from the interface's pointer, get the address of vftable.
3. VirtualProtect at least second entry of vftable for read/write.
4. Prepare your own AddRef function using c-prototype with first parameter (which is pointer to 'this'). The function must store 'this' parameter, and then call original AddRef (and let it return it's value).
5. Patch the second entry of vftable with address of your AddRef, and restore original virtualprotect mode.
6. Release your instance of IMessageFolder.
Now you have patched the vftable of IMessageFolder with your own 'AddRef' function - so, since this interface is located in msoe, which also contains many other working objects, your releasing will not cause msoe to unload. And as a result, your hook will live and any future using of this interface will firstly invoke this your interface hook (since AddRef is essentially important part of COM) - and give you the pointer to their class ('this').
(Note, that I am describe you the simple model without any little details - actually you may want to hook also Release method and track the lifetime of your traced interfaces)
Now you will always have the weak (i.e., not "addrefed") pointer to the latest instance of IMessageFolder. And since this interface is used exclusively by OE, this pointer is the actual pointer to active messagelist.
- so, now you don't need any hooks, tracks, "mirroring" and etc. Just take this latest stored value of real IMessageFolder and get your data directly from it.
-- modified at 12:26 Thursday 6th April, 2006
|
|
|
|
|
How to create IMessageTable from IMessageFolder? IMessageFolder can be Created by using CoCreateInstance, but how to create IMessageTable?
|
|
|
|
|
AFAIK there is no easy way. IMessageFolder is the 'generic' IStoreFolder (i.e., it reprepents _any_ folder in OE, not only those in local storage, but also IMAP, NNTP and another server-based folders).
IMessageTable is simple set of 'rows' you see in OE's tree. It works together with IMessageList interface and can be gеt from it using method GetMessageTable. IMessageTable may work also with 'composite' rows - collapsed and expanded subtrees of connected messages.
However... why do you need to transform IMessageFolder to IMessageTable? Is it not enough metnods in IMessageFolder itself? Or you have a specific task especially for IMessageTable? If you work with selected messages it is more comfortable just to get the list of selected\highlighted messageids, and then work with them using any interface which may work - including IMessageFolder.
IMessageList->GetSelected - you will get list of messages
IMessageTable->GetMessageIdList - with previous list will "expand" all collapsed rows into set of messageids
then
IMessageTable->GetRowFolderId - for a message will give the folderid of it. So, you will know all message's 'coordinates': folderid and messageid.
then
IMessageStore->OpenFolder - with your folderid will make the IMessageFolder of the folder you interested in,
and finally -
IMessageFolder->CopyMessage, DeleteMessage and so on...
|
|
|
|
|
thanks for your answer rapidly.I had misreaded your means,Now the new question is I don't know IMessageList CLSID and IDD,Please tell me them or how to get them.
|
|
|
|
|
Look inside resources of msoe.dll. It is included in "reginst" section:
These values I've found there:
// {233A9692-667E-11D1-9DFB-006097D50408}
DEFINE_GUID(CLSID_MessageList, 0x233A9692, 0x667E, 0x11D1, 0x9D, 0xFB, 0x0, 0x60, 0x97, 0xD5, 0x04, 0x08);
// {DE89B950-A597-11D1-9E46-006097D50408}
DEFINE_GUID(IID_IMessageList, 0xDE89B950, 0xA597, 0x11D1, 0x9E, 0x46, 0x0, 0x60, 0x97, 0xD5, 0x04, 0x08);
|
|
|
|
|
Sorry, I must be completely in the dark about something. Where is the interface definition of IMessageList and IMessageStore? My headers only show definitions for things like IStoreFolder and IStoreNamespace. Help would be appreciated.
|
|
|
|
|
This is NO publicly available interface definitions. They are internally used by MS OE and are the "general" of IStoreFolder and IStoreNamespace. I can just suggest: take the debugger and debugging symbols and dig into OE. I don't sure that this way is legal in the sense of OE's EULA, but for the moment it is the only "way of jedi".
|
|
|
|
|
Thanks for the info. My kung fu is not advanced enough to extract vtable definitions from PDB files, yet. I tried dia2dump and couldn't make heads or tails of the output. Can someone give me pointers on how I could do this?
|
|
|
|
|
|
How can I get IMessageList? I tried CoCreateInstance but the object I get seems uninitialized.
Best regards,
Grzegorz Niemirowski
|
|
|
|
|
Try this way: as early as the Outlook Express loads, call CoCreateInstance for IMessageList - and then hook the vtbl of the interface with your own proxies over IUnknown's methods. Then you can release the IMessageList - and then every time when OE itself will instantiate it, you will have actual pointer to it's instance from your proxies.
|
|
|
|
|
It works, thanks
|
|
|
|
|
But what if messages are grouped by conversation? Using you description I get all IDs messed up.
|
|
|
|
|
What do you mean by "messed up"? The IMessageList contains the messages in terms of how they are shown on the screen - either grouped, or not. They have their special inndexes which have no meaning outside their' IMessageList and connected with it IMessageTable. All you need is to call GetMessageIdList - and you will have actual MESSAGEID's, which are global by meaning.
|
|
|
|
|
My goal is to change icon of messages having particular Content-Type. After calling GetMessageIdList I open each message and check Content-Type. If it is what I'm looking for I remember the message id. Having all IDs of messages with given Content-Type I replace their icons in ATL:SysListView32 in OE. It works OK. Now I change to groupped/threaded view and see that icons were assigned to different messages than in "linear" view. It seems that in threaded view items in ListView have different IDs than IDs of messages their represent.
|
|
|
|
|
Hm, it is interested. I want to check it!
However - how to switch into grouped/threaded view in OE/WinMail? I've tried to find it promptly, but no success...
It is possible that in such view you have the case of not usual, but "search" folder - where messed messages from different folder together. So, you heed to ask for FolderId for every fow, not only for MessageId. (it is via GetRowFolderId).
I guess that messing might be when these things (folderids) are different - i.e. when you have message 1 from folder 1 connected with message 2 from folder 2 - looking just once for GetRowFolderId, it is easy to get message 2 of folder 1 (instead of of folder 2).
|
|
|
|
|
Select View -> Current View -> Group messages by converstation.
I think I found source of the problem. In threaded mode OE doesn't add all messages to the list view but only the root ones. When you expand thread OE adds new items to the list view. Let's say you have three messages and the second one has to have different icon. But it is in thread with the first one, e.g. it's a reply to first messages. You start in threaded mode where the thread is collapsed. You see only first and third message. You change icon of second message. And what happens? Third message gets new icon! Now you expand the thread. Second message gets new icon and third message gets old icon. Now you collapse the thread. Second message dissappears, no change to third message. So in threaded mode you get correct icons when all threads are expanded.
And small clarification. When I create list of icons to change I use indexes of items in array returned by GetMessageIdList, not actual MsgIds in particular folder. ListView doesn't know about IDs, it just operates on indexes from 0 to n-1. It causes problem in threaded mode because every message hidden in collapsed (actually not expaned at least once in time) thread causes shift of items and ListView contains (before expanding all threads) less messages than there are currently in a folder.
|
|
|
|
|
Ah, I see. I think your guess is very like the truth. If a message is not visible on the screen (i.e., it is collapsed inside the thread), it will not be shown as an item in the list view, and so, will not has an index.
I believe (but it is necessary to strickly check) that actual MessageID (or pointer to the struct MESSAGEINFO) of the message connected to an element in the ListView is the field "Userdata" of the ListView item (at least this way organized folders in the treeview on the left side). However, this is only my guess. It the guess right, you can just take the elem of ListView, take it's "userdata" field and directly access the message itself using this data.
It is also interesting how Outlook/Winmail itself works with message icons. I think it would be good thing to investigate .
|
|
|
|
|