全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:7577
推到 Plurk!
推到 Facebook!

MAPI example code for getting folders and messages

 
axsoft
版主


發表:681
回覆:1056
積分:969
註冊:2002-03-13

發送簡訊給我
#1 引用回覆 回覆 發表時間:2003-01-08 17:41:26 IP:61.218.xxx.xxx 未訂閱

MAPI example code for getting folders and messages

(c) 2002 Lucian Wischik. This code is free, and anyone can do with it whatever they like (except sell it or claim ownership). 資料來源:http://www.wischik.com/lu/programmer/mapi_utils.html This code is concerned with helping you traverse the folders in an MAPI message-store, and retrieve the message-content either both in plain text and in HTML. Typically the message-store will be the "Personal Folders" in Microsoft Outlook. But it also works with multiple profiles and multiple message-stores, and should also (untested) work with other Extended MAPI implementations. It also helps you traverse the folders in a standalone PST file. The code works (has been tested) with Outlook'97, with Outlook2000 (both Internet Mail Only mode and Corporate/Workgroup mode), and with outlookXP. The code consists of seven functions, written in C for Extended MAPI. I use it as a unit (mapi_utils.cpp, mapi_utils.h) added to my own project. This web page contains examples of how to use the seven functions plus extra example code, documentation for the functions, the header file and the source code for them. You should copy the code and paste it into your own project. The code uses STL, the standard template library. I wrote it under Borland C㈱ꊕ힫 5, but it should work on most compilers without too much fuss.
The following example shows how MAPI-implementations, profiles, message-stores and folders fit together.       |
    MAPI implementation provided by MS Outlook
   |   Microsoft Outlook main profile
   |  |    My imap account
   |  |   |    Inbox folder
   |  |   |    Archive folder
   |  |   |   |    Work folder
   |  |   |   |    Play folder
   |  |   |    Outbox folder
   |  |    A second messagestore
   |  |        Folder
   |   Another profile belonging to MS Outlook
   |       A messagestore
   |
    MAPI implementation provided by RivalVendor
       Main profile in RivalVendor
      |    Main messagestore
      |        Inbox folder
       Second profile in RivalVendor
Technically, nowadays, any implementation of Extended MAPI is supposed to put a key in HKEY_LOCAL_MACHINE\ SOFTWARE\ Clients\ Mail\ \ DLLPathEx. Outlook XP does this. But the older versions of Outlook don't, and I don't imagine that anyone else does. This code will work with Outlook if it is installed (even if it is an old version without DLLPathEx). And also, if any other Extended MAPI implementation is installed and has a DLLPathEx, then it will work with that as well. Note that this code loads the appropriate MAPI library at runtime. Therefore, you should not make any calls to MAPI functions directly: you should only call functions that have been GetProcAddress'd from the library. This is especially important for the MAPI utility functions (HrQueryAllRows, FreeProws &c.) which are not even implemented in Outlook'97 — if you tried to use them directly and not GetProcAddress'd, then your code would fail at load-time and wouldn't even start. The code below uses GetProcAddress for the standard MAPI functions, and provides its own implementation of the utility functions: pHrQueryAllRows, pFreeProws &c. To retrieve the HTML content of a message is not documented by Microsoft for extended-MAPI. The code below for retrieving HTML text has worked on the thousand or so messages I've tried it on, but I can't guarantee it will work for everything. Microsoft claim that Extended MAPI is 'unsupported' for Outlook 2000 in Internet Mail Only mode. Nevertheless, it mostly all works. The only thing that fails is loading a standalone PST file. But for Outlook'97 and OutlookXp this isn't an issue — neither of these use the crippled Internet Mail Only mode. I acknowledge with gratitude the help from people on the newsgroup microsoft.public.win32.programmer.messaging, and especially the MS reps on that newsgroup. Examples of how to program with itTo get a list of all message-stores. The following code retrieves every profile/storename pair for the default x-mapi implementation. (Or, if the default email client doesn't support x-mapi, as Outlook Express doesn't, then it retrieves it for the first one that does.)
mapi_EnsureLibraries();
if (mapi_Libraries.size()>0) // we require at least one mapi-x implementation
{ mapi_EnsureStores(mapi_Libraries.front().path);
  for (list::const_iterator j=mapi_Stores.begin(); j!=mapi_Stores.end(); j)
  { if (j->type==mstStore)
    { //... do something with j->profile and j->store
    }
  }
}
To get default profile and message-store. The following code retrieves the default profile/storename for the default x-mapi:
string profile="", storename="";
mapi_EnsureLibraries();
if (mapi_Libraries.size()>0)
{ mapi_EnsureStores(mapi_Libraries.front().path);
  list::const_iterator j=mapi_Stores.begin(); j;
  if (j->type==mstStore)
  { profile=j->profile; storename=j->store;
    // ... and do something with them
  }
}
To iterate over all folders. The following code code, given a profile/storename, retrieves the list of folders in it, and iterates over all the folders. It assumes a variable 'hwnd' which is the window-handle of the application's main window.
mapi_EnsureFolders(hwnd,profile,storename);
for (list::const_iterator i=mapi_Folders.begin(); i!=mapi_Folders.end(); i)
{ IMAPIFolder *ifolder; ULONG ftype;
  HRESULT hr = mapi_msgstore->OpenEntry(i->eid.size,i->eid.ab, NULL,0,&ftype,(IUnknown**)&ifolder);
  if (hr==S_OK)
  { if (ftype==MAPI_FOLDER)
    { // ... and do something with the ifolder, like iterating over its messages
    }
    ifolder->Release();
  }
}
To load a PST file. The following code, given the filename of a PST file, retrieves the list of folders in it. As before, it assumes 'hwnd'.
mapi_EnsureFolders(hwnd,"c:\\temp\\mystuff.pst");
for (list::const_iterator i=mapi_Folders.begin(); i!=mapi_Folders.end(); i)
{ IMAPIFolder *ifolder; ULONG ftype;
  HRESULT hr = mapi_msgstore->OpenEntry(i->eid.size,i->eid.ab, NULL,0,&ftype,(IUnknown**)&ifolder);
  if (hr==S_OK)
  { if (ftype==MAPI_FOLDER)
    { // ... and do something with the ifolder, like iterating over its messages
    }
    ifolder->Release();
  }
}
To iterate over messages in a folder. The following code, given a pointer 'ifolder' to an IMAPIFolder, iterates over all the messages in it.
IMAPITable *contents;
HRESULT hr = ifolder->GetContentsTable(0,&contents);
if (hr==S_OK)
{ SizedSPropTagArray(4,foldcols) = {4, {PR_ENTRYID,PR_SUBJECT,PR_SENT_REPRESENTING_NAME,PR_MESSAGE_DELIVERY_TIME} };
  SRowSet *frows;
  hr = pHrQueryAllRows(contents,(SPropTagArray*)&foldcols,NULL,NULL,0,&frows);
  if (hr==S_OK)
  { for (unsigned int m=0; mcRows; m)
    { // We'll first retrieve the basic header information of the message.
      // We must do this now, from the contents-table, because if the
      // message is offline and we're using Outlook2000/IMO then it's not
      // possible to IMessage::GetProps.
      // NB. In this headers, "sent-representing" is available, but none of the
      // other sender properties are.
      mapi_TEntryid eid; string subj,sndr,date;
      if (frows->aRow[m].lpProps[0].ulPropTag==PR_ENTRYID) eid=&frows->aRow[m].lpProps[0];
      if (frows->aRow[m].lpProps[1].ulPropTag==PR_SUBJECT) subj=frows->aRow[m].lpProps[1].Value.lpszA;
      if (frows->aRow[m].lpProps[2].ulPropTag==PR_SENT_REPRESENTING_NAME) sndr=frows->aRow[m].lpProps[2].Value.lpszA;
      if (frows->aRow[m].lpProps[3].ulPropTag==PR_MESSAGE_DELIVERY_TIME)
      { FILETIME ft; FileTimeToLocalFileTime(&frows->aRow[m].lpProps[3].Value.ft, &ft); // Translate from UTC to current timezone
        SYSTEMTIME st; FileTimeToSystemTime(&ft,&st);
        const char *days[] = {"Sun ","Mon ","Tue ","Wed ","Thu ","Fri ","Sat "};
        const char *day=0; if (st.wDayOfWeek<=6) day=days[st.wDayOfWeek];
        const char *months[] = {"","Jan ","Feb ","Mar ","Apr ","May ","Jun ","Jul ","Aug ","Sep ","Oct ","Nov ","Dec "};
        const char *month=0; if (st.wMonth>=1 && st.wMonth<=12) month=months[st.wMonth];
        char c[20]; wsprintf(c,"%s%i %s%i",day,st.wDay,month,st.wYear); date=c;
      }
      string message_details = sndr" - "닦" - "疫;
      // Now, 'message_details' shows the important stuff about the message,
      // so we could use it in (e.g.) error reports if some other bit of code fails.
      if (!eid.isempty())
      { IMessage *imsg; ULONG msgtype;
        hr = ifolder->OpenEntry(eid.size, eid.ab, NULL, 0, &msgtype, (IUnknown**)&imsg);
        if (hr==S_OK)
        { if (msgtype==MAPI_MESSAGE)
          { //
            // do something with the imsg here, like read its body...
            //
          }
          imsg->Release();
        }
      }
    }
    pFreeProws(frows);
  }
  contents->Release();
}
To retrieve message text and properties. The following code, given a pointer 'imsg' to an IMessage, retrieves the body of the message in both text and (if present) html, and also retrieves its 'from' and 'to' information, and determines whether the message is available offline.
bool AvailableOffline=false;
SizedSPropTagArray(8, mcols) = {8,
        {PR_MESSAGE_CLASS, PR_SENDER_NAME, PR_SENDER_EMAIL_ADDRESS, PR_RTF_IN_SYNC,
         PR_RECEIVED_BY_EMAIL_ADDRESS, PR_RECEIVED_BY_NAME, PR_RECEIVED_BY_ENTRYID, PR_TRANSPORT_MESSAGE_HEADERS}};
ULONG pcount; SPropValue *props=0; HRESULT hr;
hr = imsg->GetProps((SPropTagArray*)&mcols,0,&pcount,&props);
// Outlook2000 in IMO gives this NO_ACCESS error messages that are header-only.
// But Outlook97, and Outlook2000/CWG, and OutlookXP, don't give the error...
// we'll test for them later on.
if (hr==MAPI_E_NO_ACCESS) AvailableOffline=false;
bool okay=true;
if (hr!=S_OK && hr!=MAPI_W_ERRORS_RETURNED) okay=false;
if (okay && props[0].ulPropTag!=PR_MESSAGE_CLASS) okay=false;
if (okay && (strncmp(props[0].Value.lpszA,"IPM.Note",8)!=0 && strncmp(props[0].Value.lpszA,"IPF.Note",8)!=0)) okay=false;
if (!okay) {if (props!=0) pMAPIFreeBuffer(props); return; }    // Was it a received message, or one that the user sent?
// (This isn't recorded explicitly. We'll use a heurstic instead:
// if there's any sign that this message has been over the internet,
// then presumably it's received. If there's no such sign, then
// presumably it's just the local copy put into the 'Sent' folder)
bool isrcvd = false;
if (props[4].ulPropTag==PR_RECEIVED_BY_EMAIL_ADDRESS) isrcvd=true;
if (props[5].ulPropTag==PR_RECEIVED_BY_NAME) isrcvd=true;
if (props[6].ulPropTag==PR_RECEIVED_BY_ENTRYID) isrcvd=true;
if (props[7].ulPropTag==PR_TRANSPORT_MESSAGE_HEADERS) isrcvd=true;    // Retrieve the 'from' property
// in the form Name 
string from;
if (props[1].ulPropTag==PR_SENDER_NAME) from=props[1].Value.lpszA;
if (props[2].ulPropTag==PR_SENDER_EMAIL_ADDRESS)
{ if (from!="") from=" ";
  from = string("<")ꚺ[2].Value.lpszA">";
}
if (from!="") AvailableOffline = true;    // Retrieve the 'to' property
// in the form Name , AnotherName 
string to;
IMAPITable *rtable;
hr = imsg->GetRecipientTable(0,&rtable);
if (hr==S_OK)
{ SizedSPropTagArray(3,rcols) = {3, {PR_DISPLAY_NAME,PR_EMAIL_ADDRESS,PR_RECIPIENT_TYPE} };
  SRowSet *rrows;
  hr = pHrQueryAllRows(rtable,(SPropTagArray*)&rcols,NULL,NULL,0,&rrows);
  if (hr==S_OK)
  { for (unsigned int r=0; rcRows; r)
    { string recipient;
      if (rrows->aRow[r].lpProps[0].ulPropTag==PR_DISPLAY_NAME) recipient=rrows->aRow[r].lpProps[0].Value.lpszA;
      if (rrows->aRow[r].lpProps[1].ulPropTag==PR_EMAIL_ADDRESS)
      { if (recipient!="") recipient=" ";
        recipient = string("<")꺺>aRow[r].lpProps[1].Value.lpszA">";
      }
      if (recipient!="")
      { if (to!="") to=", ";
        to = recipient;
      }
    }
    pFreeProws(rrows);
  }
  rtable->Release();
}
if (to!="") AvailableOffline = true;    // Get the body of the message as plain text
// into the buffer 'bodybuf'
char *bodybuf=0; unsigned int bodysize=0;
IStream *istream;
hr = imsg->OpenProperty(PR_BODY, &IID_IStream, STGM_READ, 0, (IUnknown**)&istream);
if (hr==S_OK)
{ AvailableOffline = true;
  STATSTG stg = {0};
  hr = istream->Stat(&stg,STATFLAG_NONAME);
  if (hr==S_OK)
  { bodysize = stg.cbSize.LowPart; // won't bother checking for >2gb messages!
    bodybuf = new char[bodysize];
    ULONG red; hr = istream->Read(bodybuf, bodysize, &red);
    if (hr!=S_OK) bodysize=0;
    else if (red<bodysize) bodysize=red;
    bodybuf[bodysize]=0;
  }
  istream->Release();
}    // Get the body of the message if it was in HTML
// into the buffer 'htmlbuf'
char *htmlbuf=0; unsigned int htmlsize=0;
hr = imsg->OpenProperty(PR_BODY_HTML, &IID_IStream, STGM_READ, 0, (IUnknown**)&istream);
if (hr==S_OK)
{ STATSTG stg = {0};
  hr = istream->Stat(&stg,STATFLAG_NONAME);
  if (hr==S_OK)
  { htmlsize = stg.cbSize.LowPart;
    htmlbuf = new char[htmlsize];
    ULONG red; hr = istream->Read(htmlbuf, htmlsize, &red);
    if (hr!=S_OK) htmlsize=0;
    else if (red<htmlsize) htmlsize=red;
    htmlbuf[htmlsize]=0;
  }
  istream->Release();
}
// In actual fact, the PR_HTML_BODY that we just tested is rarely used.
// More frequently, Microsoft encode the html message source into the RTF...
// First we have to sync the rtf with the body, in case the messsagestore hasn't already.
// (nb. it would have made more sense to query the support mask just once, rather than
// for each message as we're doing here)
SPropValue *svmask; bool mustsync=true;
hr = pHrGetOneProp(mapi_msgstore, PR_STORE_SUPPORT_MASK, &svmask);
if (hr==S_OK)
{ if ((svmask->Value.ul&STORE_RTF_OK)!=0) mustsync=false;
  else if (props[3].ulPropTag==PR_RTF_IN_SYNC && props[3].Value.b!=0) mustsync=false;
  pMAPIFreeBuffer(svmask);
}
if (mustsync)
{ BOOL isupdated; pRTFSync(imsg,RTF_SYNC_BODY_CHANGED,&isupdated);
  if (isupdated) imsg->SaveChanges(0);
}
// Now it's synced if necessary. We can retrieve the RTF property
hr=S_FALSE; if (htmlbuf==0) hr = imsg->OpenProperty(PR_RTF_COMPRESSED, &IID_IStream, STGM_READ, 0, (IUnknown**)&istream);
if (hr==S_OK)
{ AvailableOffline = true;
  IStream *iunstream; // for the uncompressed stream
  pWrapCompressedRTFStream(istream,0,&iunstream);
  // bufsize is the size of the buffer we've allocated, and htmlsize is the
  // amount of text we've read in so far. If our buffer wasn't big enough,
  // we enlarge it and continue. We have to do this, instead of allocating
  // it up front, because Stream::Stat() doesn't work for the unc.stream
  unsigned int bufsize=10240; htmlbuf = new char[bufsize];
  htmlsize=0; bool done=false;
  while (!done)
  { ULONG red; hr = iunstream->Read(htmlbuf蛙ꖲⳞ, bufsize-htmlsize, &red);
    if (hr!=S_OK) {htmlbuf[htmlsize]=0; done=true;}
    else
    { htmlsize=red; done = (red < bufsize-htmlsize);
      if (!done)
      { unsigned int newsize=2*htmlsize; char *newbuf=new char[newsize];
        memcpy(newbuf,htmlbuf,htmlsize); delete[] htmlbuf;
        htmlbuf=newbuf; bufsize=newsize;
      }
    }
  }
  htmlbuf[htmlsize]=0;
  iunstream->Release();
  istream->Release();
  // Now, assuming that this thing was an encoded RTF/HTML hybrid,
  // we can extract the original html.
  if (htmlbuf!=0)
  { if (!isrtfhtml(htmlbuf,htmlsize)) {delete[] htmlbuf; htmlbuf=0;}
    else decodertfhtml(htmlbuf,&htmlsize);
  }
}    // Check if there are any attachments
IMAPITable *atable;
hr = imsg->GetAttachmentTable(0,&atable);
if (hr==S_OK)
{ SizedSPropTagArray(3,acols) = {3, {PR_ATTACH_SIZE,PR_ATTACH_NUM,PR_ATTACH_METHOD }};
  SRowSet *arows;
  hr = pHrQueryAllRows(atable,(SPropTagArray*)&acols,NULL,NULL,0,&arows);
  if (hr==S_OK)
  { if (arows->cRows>0) AvailableOffline = true;
    pFreeProws(arows);
  }
  atable->Release();
}    // AvailableOffline? We have set this boolean variable through the preceeding
// code, using the following heuristic: if there is any content to the message
// at all be it To: or From: lines, or body, or attachments), then it must have
// been downloaded. The only way this could fail, is if the user composed a
// draft message but then closed it while it was still completely empty.
// Why do we resort to such a grubby heuristic? Because Microsoft do not define
// any other way to tell whether a message is offline. OutlookXP uses some
// named properties that seem to contain the information, but they're not
// documented and therefore can't be trusted.    if (AvailableOffline)
{ //
  // Now, do something with the message contents!
  //
}    if (bodybuf!=0) delete[] bodybuf;
if (htmlbuf!=0) delete[] htmlbuf;
pMAPIFreeBuffer(props);
Instructions on how to use the functionsFirst, call mapi_EnsureLibraries(). This will build a list of all the extended-MAPI implementations on the system. The list goes in the global list "mapi_Libraries". If the default email client supports extended-MAPI, then it appears first in the list. The list might be empty, if there's no extended-MAPI implementation installed on your computer. Second, call mapi_EnsureStores(lib-path). This will build a list of all profiles and all message-stores belonging to this implementation. This goes in the global list "mapi_Stores". This is a heterogenous list containing entries for both profiles and message-stores. It is grouped so that all the message-stores for a particular profile come immediately after the entry for that profile. Thus, for the above example, "Main-Profile, my-imap-acc, 2nd-msgstore, Another-profile, a-msgstore" The default profile comes first in the list. And within each profile, the default message-store comes first. You can call mapi_EnsureStores with a different library at any time. This will free the previous library. Third, call mapi_EnsureFolders(hwnd,prof,store). This will build a list of all email folders in the store. Again, the list is flattened. The above example would be "Inbox, Archive, Archive\Work, Archive\Play, Outbox". Alternatively, mapi_EnsureFolders(hwnd,pst_fn). This will build a list of the email folders in the specified PST file. Incidentally, this will only work if the lib-path in mapi_EnsureStores(lib-path) points to an MAPI implementation that supports PST files. Also, the "mapi_session" global variable is initialized by these calls. It has type IMAPISession*, and you can use it to open folders and stuff. Note, incidentaly, that both mapi_EnsureFolder calls take an HWND as their first argument. This is because, perhaps, the user might have to supply a password to logon to the message-store. The password dialog will appear as a modal child of hwnd. You can call mapi_EnsureFolders with a different store/pst at any time. This will free the previous stuff. Fourth, you'll have to iterate over the folders yourself, to do what you want with them. To make things easier, each folder is annotated with its entry-id: this makes it a simple task to just do mapi_session->OpenEntry(eid.size,eid.ab,...,&ifolder); Also, each folder is annotated with its type: whether it's inbox, or outbox, or whatever. Note incidentally that "Drafts" is not distinguished from the other normal mail folders. Finally, when finished, mapi_EnsureFinished() will free everything. Note: The code uses global static variables to remember its state. You cannot have one part of your program working with one store while at the same time another part works with a different store, for instance. Also, each procedure is called "EnsureXXX" to indicate that it doesn't matter if you call it redundantly.

Header-file 'mapi_utils.h'#ifndef mapi_utilsH
#define mapi_utilsH    void mapi_EnsureLibraries();
void mapi_EnsureStores(const string libpath);
void mapi_EnsureFolders(HWND h, const string profile, const string store);
void mapi_EnsureFolders(HWND h, const string pst_fn);
void mapi_EnsureFinished();    bool isrtfhtml(const char *buf,unsigned int len);
void decodertfhtml(char *buf,unsigned int *len);        class mapi_TEntryid
{ public:
  unsigned int size;
  ENTRYID *ab;
  mapi_TEntryid() {ab=0;size=0;}
  mapi_TEntryid(SPropValue *v) {ab=0;size=0; if (v->ulPropTag!=PR_ENTRYID) return; set(v->Value.bin.cb,(ENTRYID*)v->Value.bin.lpb);}
  mapi_TEntryid(mapi_TEntryid const &e) {ab=0;size=0; set(e.size,e.ab);}
  mapi_TEntryid(unsigned int asize,ENTRYID *eid) {ab=0;size=0; set(asize,eid);}
  mapi_TEntryid &operator= (const mapi_TEntryid *e) {set(e->size,e->ab); return *this;}
  mapi_TEntryid &operator= (const SPropValue *v) {set(0,0); if (PROP_TYPE(v->ulPropTag)!=PT_BINARY) return *this; set(v->Value.bin.cb,(ENTRYID*)v->Value.bin.lpb); return *this;}
  ~mapi_TEntryid() {set(0,0);}
  void set(unsigned int asize, ENTRYID *eid) {if (ab!=0) delete[] ((char*)ab); size=asize; if (eid==0) ab=0; else {ab=(ENTRYID*)(new char[size]);memcpy(ab,eid,size);}}
  void clear() {set(0,0);}
  bool isempty() const {return (ab==0 || size==0);}
  bool isequal(IMAPISession *sesh, mapi_TEntryid const &e) const
  { if (isempty() || e.isempty()) return false;
    ULONG res; HRESULT hr = sesh->CompareEntryIDs(size,ab,e.size,e.ab,0,&res);
    if (hr!=S_OK) return false;
    return (res!=0);
  }
};    typedef struct {string name, path; bool supported;} mapi_TLibraryInfo;    enum mapi_TFolderType {mftInbox,mftOutbox,mftSent,mftDeleted,mftCalendar,mftContacts,mftJournal,mftNotes,mftTasks,mftSpecial,mftMail,mftStuff};
typedef struct {int depth; string name, path; mapi_TFolderType type; mapi_TEntryid eid;} mapi_TFolderInfo; // nb. path is the complete thing, and name is just the final bit of it    enum mapi_TStoreType {mstProfile, mstProfileSecret, mstStore};
typedef struct {string profile, store; mapi_TStoreType type;} mapi_TStoreInfo;    // These are initialized by mapi_EnsureLibraries()
extern list mapi_Libraries;
// These are initialized by mapi_EnsureStores(lib)
extern list mapi_Stores;
// And so are these mapi functions
typedef HRESULT (STDMETHODCALLTYPE RTFSYNC)(LPMESSAGE lpMessage, ULONG ulFlags, BOOL FAR *lpfMessageUpdated);
typedef HRESULT (STDMETHODCALLTYPE WRAPCOMPRESSEDRTFSTREAM)(LPSTREAM lpCompressedRTFStream, ULONG ulFlags, LPSTREAM FAR *lpUncompressedRTFStream);
extern RTFSYNC *pRTFSync;
extern WRAPCOMPRESSEDRTFSTREAM *pWrapCompressedRTFStream;
extern MAPIFREEBUFFER *pMAPIFreeBuffer;
// These are initialized by mapi_EnsureFolders(storeinfo)
extern IMAPISession *mapi_session;    // The session
extern IMsgStore *mapi_msgstore;      // The message store
extern list mapi_Folders;
// And all are freed, if necessary, by mapi_EnsureFinished.        // I must implement these utility functions myself. That's because
// they're not present in Outlook97's version of mapi32.dll.
HRESULT pHrGetOneProp(LPMAPIPROP lpMapiProp, ULONG ulPropTag, LPSPropValue FAR *lppProp);
void pFreeProws(LPSRowSet lpRows);
HRESULT pHrQueryAllRows(LPMAPITABLE lpTable, LPSPropTagArray lpPropTags, LPSRestriction lpRestriction, LPSSortOrderSet lpSortOrderSet, LONG crowsMax, LPSRowSet FAR *lppRows);    // These were omitted from the standard headers
#ifndef PR_BODY_HTML
#define PR_BODY_HTML (PROP_TAG(PT_TSTRING,0x1013))
#endif    #ifndef PR_ATTACH_CONTENT_ID
#define PR_ATTACH_CONTENT_ID (PROP_TAG(PT_TSTRING,0x3712))
#endif    #ifndef PR_ATTACH_CONTENT_LOCATION
#define PR_ATTACH_CONTENT_LOCATION (PROP_TAG(PT_TSTRING,0x3713))
#endif    #ifndef PR_ATTACH_FLAGS
#define PR_ATTACH_FLAGS (PROP_TAG(PT_LONG,0x3714))
#endif    #ifndef PR_ATTACH_TRANSPORT_NAME
#define PR_ATTACH_TRANSPORT_NAME (PROP_TAG(PT_TSTRING,0x370C))
#endif    #ifndef PR_ATTACH_MIME_SEQUENCE
#define PR_ATTACH_MIME_SEQUENCE (PROP_TAG(PT_LONG,0x3710))
#endif    #ifndef PR_SMTP_MESSAGE_ID
#define PR_SMTP_MESSAGE_ID (PROP_TAG(PT_TSTRING,0x1035))
#endif        #endif    Source-code 'mapi_utils.cpp'#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
#pragma hdrstop // precompiled headers stop here
#include "mapi_utils.h"        list mapi_Libraries;  // from the registry, a list of ex-mapi dlls
bool got_libraries=false;                // have we built that list yet?
//
HINSTANCE hmapilib=0;             // for loadlibrary(mapi32.dll). If this is non-null, we must freelibrary it at the end.
string mapi_lib_path;             // this is the pathname of what we've currently loaded.
list mapi_Stores;
//
MAPIADMINPROFILES *pMAPIAdminProfiles=0;
MAPIUNINITIALIZE *pMAPIUninitialize=0;
MAPILOGONEX *pMAPILogonEx=0;
MAPIFREEBUFFER *pMAPIFreeBuffer=0;
RTFSYNC *pRTFSync=0;
WRAPCOMPRESSEDRTFSTREAM *pWrapCompressedRTFStream=0;
//
IMAPISession *mapi_session=0;     // The session
string mapi_session_profile;      // This is the profile name which session is logged onto.
IMsgStore *mapi_msgstore=0;       // The message store
string mapi_msgstore_name;        // This is the name to which that message-store refers
list mapi_Folders;  // a list of the folders
bool got_eids = false;             // a shortcut for whether or not all the following have been set
mapi_TEntryid eid_inbox, eid_outbox, eid_sent, eid_deleted;
mapi_TEntryid eid_calendar, eid_contacts, eid_journal, eid_notes, eid_tasks;    void mapi_EnsureCommonEids();
mapi_TFolderType mapi_GetFolderType(mapi_TEntryid &eid, IMAPIFolder *f);
void mapi_RecEnsureFolders(IMAPIFolder *parent, int depth, string prefix, list *folders);
void mapi_EnsureCrazyProfileDeleted(IProfAdmin *iprofadmin);
list mapi_RegQuerySubkeys(HKEY key);
string mapi_RegQueryString(HKEY key,const string name);    // MAPI_ENSURELIBRARIES -- builds up a list of all the Extended MAPI libraries
// that have been installed on this machine, in the global mapi_Libraries
// list. This is a difficult task...
// In the olden days, there didn't exist such a list anywhere on the system.
// Very recently (with the advent of Outlook XP) it has introduced the
// idea that there should be a list under HKEY_LOCAL_MACHINE\Clients\Mail
// whereby every extended-MAPI is indicated with a "DLLPathEx" value.
// But in the olden days (with Outlook2000 in both IMO and CWG modes, and
// with Outlook97) it merely has a "DLLPath" value.
// So our plan is as follows:
// (1) For any DLLPathEx keys, we'll add them.
// (2) If one of those DLLPathEx happened to be for the "Microsoft Outlook"
// service, then it must have been XP or later, and so we can return immediately.
// (3) If Microsoft Outlook was not even installed (i.e. not in the list), then we
// can also return immediately.
// (4) Otherwise, there must have been Outlook97 or 2000 installed. So we
// will set a key under HKEY_LOCAL_MACHINE\SOFTWARE\Microsft\Windows Messaging Subsystem\MSMapiApps
// to indicate that our app will use the "Microsoft Outlook" mapi. Then, we
// add "mapi32.dll" into the list. On a modern system, when you LoadLibrary(mapi32.dll),
// this is merely a stubb: it actually looks into that list and figures out which mapi
// to use from there. Therefore, this will end up loading Outlook.
//
void mapi_EnsureLibraries()
{ if (got_libraries) return;
  got_libraries=true;
  //
  // First, if the mapi stub has been installed, then we can check in the
  // registry for which MAPI libraries are present on this machine.
  bool uses_stub=false, IsOutlookInstalled=false, IsOutlookExListed=false;
  HKEY key; LONG res;
  res = RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Clients\\Mail",0,KEY_READ,&key);
  if (res==ERROR_SUCCESS)
  { uses_stub=true;
    string defname = mapi_RegQueryString(key,"");   // Find out which one is the default
    list names = mapi_RegQuerySubkeys(key); // Get the list of child keys
    RegCloseKey(key);
    for (list::const_iterator i=names.begin(); i!=names.end(); i)
    { string name = *i;
      bool thisisoutlook = (name=="Microsoft Outlook");
      IsOutlookInstalled |= thisisoutlook;
      res = RegOpenKeyEx(HKEY_LOCAL_MACHINE,("SOFTWARE\\Clients\\Mail\\"鶩).c_str(),0,KEY_READ,&key);
      if (res==ERROR_SUCCESS)
      { // Get the path, stored in "DLLPathEx"
        string path = mapi_RegQueryString(key,"DLLPathEx");
        if (path!="")
        { if (thisisoutlook) IsOutlookExListed=true;
          mapi_TLibraryInfo lib; lib.name=name; lib.path=path; lib.supported=true;
          if (name==defname) mapi_Libraries.push_front(lib); else mapi_Libraries.push_back(lib);
        }
        RegCloseKey(key);
      }
    }
  }
  if (IsOutlookInstalled && IsOutlookExListed) return; // outlook XP is fine as it is.
  //
  // If it uses the stub technique, and Outlook is installed, we can set the registry
  // key to tell the stub to give us outlook
  if (uses_stub && IsOutlookInstalled)
  { res = RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows Messaging Subsystem\\MSMapiApps",0,KEY_SET_VALUE,&key);
    if (res!=ERROR_SUCCESS) return;
    char c[MAX_PATH]; GetModuleFileName(NULL,c,MAX_PATH);
    const char *d=c닚(c)-1; while (d>c && *d!='/' && *d!='\\' && *d!=':') d--; if (*d=='/' || *d=='\\' || *d==':') d;
    res = RegSetValueEx(key,d,0,REG_SZ,(LPBYTE)"Microsoft Outlook",18);
    RegCloseKey(key);
    if (res!=ERROR_SUCCESS) return;
    mapi_TLibraryInfo outlib; outlib.name="Microsoft Outlook"; outlib.path="mapi32.dll";
    mapi_Libraries.push_back(outlib);
  }
  //
  // Otherwise, if it doesn't even use the stub, then we'll use the old-fashioned technique.
  if (!uses_stub)
  { res = RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows Messaging Subsystem",0,KEY_READ,&key);
    if (res!=ERROR_SUCCESS) return;
    DWORD type; DWORD size=10; char c[10];
    res = RegQueryValueEx(key,"MAPIX",NULL,&type,(LPBYTE)c,&size);
    RegCloseKey(key);
    if (res!=ERROR_SUCCESS) return;
    if (strcmp(c,"1")!=0) return;
    mapi_TLibraryInfo deflib; deflib.name="Default"; deflib.path="mapi32.dll";
    mapi_Libraries.push_back(deflib);
  }
}    // MAPI_ENSURESTORES -- given a path to a mapi-x DLL, this builds up a list
// of all the profiles and message-stores listed in that mapi implementation.
// The list is heterogenous. The 'type' field in each item in the list
// says whether it's a store, or a profile, or a "secret profile" (one where
// we weren't able to get a list of its stores, presumably because this
// information is protected by a password).
// Note: the PST-file support in the rest of this program uses a temporary
// profile called "Lu's Crazy Profile (democode)". Just out of neatness,
// the list we generate will not include that profile.
//
void mapi_EnsureStores(const string libpath)
{ if (mapi_lib_path==libpath) return;
  mapi_EnsureFinished();
  mapi_lib_path=libpath;
  //
  // Load the library
  hmapilib = LoadLibrary(libpath.c_str()); if (hmapilib==0) return;
  MAPIINITIALIZE *pMAPIInitialize = (MAPIINITIALIZE*)GetProcAddress(hmapilib,"MAPIInitialize");
  pMAPIAdminProfiles = (MAPIADMINPROFILES*)GetProcAddress(hmapilib,"MAPIAdminProfiles");
  pMAPILogonEx = (MAPILOGONEX*)GetProcAddress(hmapilib,"MAPILogonEx");
  pMAPIUninitialize = (MAPIUNINITIALIZE*)GetProcAddress(hmapilib,"MAPIUninitialize");
  pMAPIFreeBuffer = (MAPIFREEBUFFER*)GetProcAddress(hmapilib,"MAPIFreeBuffer");
  pRTFSync = (RTFSYNC*)GetProcAddress(hmapilib,"RTFSync");
  pWrapCompressedRTFStream = (WRAPCOMPRESSEDRTFSTREAM*)GetProcAddress(hmapilib,"WrapCompressedRTFStream");
  if (pMAPIInitialize==0 || pMAPIAdminProfiles==0 || pMAPILogonEx==0 || pMAPIUninitialize==0
     || pMAPIFreeBuffer==0 || pRTFSync==0 || pWrapCompressedRTFStream==0) {FreeLibrary(hmapilib);hmapilib=0;return;}
  HRESULT hr = pMAPIInitialize(NULL); if (hr!=S_OK) {FreeLibrary(hmapi
        
系統時間:2024-04-23 19:47:04
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!