This project is read-only.


Jun 6, 2010 at 6:31 PM

Is this a "virtual" property in MAPI?  I was attempting to fetch it from the message property bag, but it didn't exist.  Is there another/better way to get this property?

Jun 6, 2010 at 6:38 PM

Yes, it's a virtual property. It's calculated based on the node_id of each object (folder, message, etc). I believe [MS-PST] has the algorithm to calculate the entry id from a node_id.

Jul 9, 2010 at 3:19 PM

Good morning! We also needed PR_ENTRYID for an e-discovery application, and I thought it might be worth adding to the pstsdk API.

Here are two very rough patches which implement a get_entry_id function: 

Add a node::get_db() function

Compute MAPI PR_ENTRYID for messages and attachments

Please let me know if you have any suggestions! I'm happy to make changes. When you're ready, I'll send you an email with a single patch.

Jul 9, 2010 at 6:15 PM

I think adding accessors to get PR_ENTRYID is a good idea. My thoughts:

  • I don't think attachments have entry ids (since they're only unique in the context of their parents). I also see special code to ensure that if someone tries to read the entry id of an embedded message, it fails.
  • Might as well allow accessing the entryid of a folder as well.
  • Might as well allow opening a message or folder object by entryid (in addition to by node id).
  • I don't think propbag.h is the proper place to put this logic. Ideally at this level it knows nothing of MAPI specific logic. To me, it feels like it should go in a helper function at the PST layer which is called by the message and folder objects get_entryid() methods. The equivalent decode function (entryid to node_id) would exist to be used to simplify the above methods of opening a folder or message by entryid.
  • Might as well add a get_entryid() method to the pst object. Note though, that the PSTs entryid is computed differently from the message and folders entryid.

Did you verify that the PR_ENTRYID value computed by MAPI proper is the same as your implementation, btw? MFCMapi can be used to easily get a byte dump of the expected value of an entry id for a given message.

Jul 9, 2010 at 8:16 PM

One other note; the entry id calculation for OST files is different (PST/OST differences are not documented, but this is among them). Perhaps all of the get_entryid() functions should throw some exception if the underlying file is actually an OST file to avoid headaches for someone down the road?

Jul 10, 2010 at 4:56 AM

Call me crazy, but why not document the method for OST entryid generation, and incorporate it into the code!  :)

Aug 3, 2010 at 10:57 PM

These all sound like excellent ideas! A few of them may be a little bit beyond my current command of PST internals, however. I'm preparing another set of patches based on some in-house testing, and will submit them to you once we've tested them a bit.

I haven't run MFCMapi because MFC isn't included with Visual Studio Express. Is there a copy I can download somewhere?

Cheers, Eric

Aug 4, 2010 at 6:29 AM
Edited Aug 4, 2010 at 6:30 AM

MFCMapi is a stand alone application available at It's very useful in a situation like this, because you can compare the "official" output of a calculated property with a new implementation quite easily.

Aug 8, 2010 at 5:41 PM

Good morning! Here is the latest version of my PR_ENTRYID patch:

This fixes a number of problems with the previous patch:

  1. We no longer support calculating PR_ENTRYID for attachments.
  2. We raise an error if the user tries to calculate PR_ENTRYID for a submessage.
  3. All the support code now lives in the PST layer, as requested.
  4. The generated PR_ENTRYID values have now been checked against MFCMAPI, as requested. And this was a very good idea—my earlier code was outputting the last 4 bytes in big-endian order. This bug has now been fixed. Thank you for helping me get some real-world test data!

However, some of the issues raised above have not yet been addressed:

  1. I haven't added support for calculating the PST's PR_ENTRYID.
  2. I haven't added support for opening a message or folder by PR_ENTRYID.
  3. I haven't added any code to detect OST files and either (a) raise an error or (b) calculate a correct PR_ENTRYID.

Issues (1) and (2) are highly desirable features, but they're not required by our application. And since we wouldn't use them, I'd be coding somewhat blindly and I would risk writing code that fails in the real world. Would it be OK to leave the implementation of (1) and (2) to somebody who'll be using them with real data?

Issue (3) definitely needs to be addressed. Is there an easy way to tell a PST from an OST without relying on the file extension? Is there an easy way to calculate correct OST PR_ENTRYID values? And would it be possible to add a sample OST file to the 'pstsdk/test' directory? (I don't think we have any OST files that we can redistribute.)

As always, thank you for reviewing patches, and for all your advice!

Cheers, Eric

Aug 9, 2010 at 3:26 AM

Looks good. You actually don't need to add the m_is_submessage variable - you can test directly by calling is_subnode() on the underlying node. Other than that it looks good.

I'll take care of #1 and #2 at some point. As for #3, I wouldn't say it *has* to be addressed; but the easiest thing would be to expose the database_type field of the header (probably through the database_context interface). 

As far as calculating the OST entry IDs, it involves a few lookup tables which tend to change (or at least, can change) from version to version. It works hard to ensure it returns the same entry id as the server, which is the ultimately authority. OST internals aren't documented, so the goal for pstsdk is just to support them to the extent that they are the same as PST files (and not necessary address how they're different).

Aug 9, 2010 at 3:03 PM

Thank you for the feedback!

I've updated the patch so that it calls is_subnode(), and so that it raises errors if somebody asks for the PR_ENTRYID of an OST.

I'm not sure whether the 'is_pst()' function is sufficiently general here—it's enough for the needs of this patch, but you might want to expose the actual disk::database_type directly. I just didn't want to start exposing disk-level types without your prior approval. :-)

You can see our entire patch series here, including the patches you reviewed in another thread:

Once we finish testing this on Windows, 32-bit Linux and 64-bit Linux, I'll assemble all the patches into a single diff and email it to you.

Thank you for all your help!

Aug 9, 2010 at 3:32 PM

I went about checking for PST/OST a little differently ... no code changes to the core were necessary (that I recall):

template<typename T>
class database_check : public database_impl<T>
database_check(const wchar_t *filename) : database_impl<T>(filename) {}

bool is_ost()
   return (m_header.wVerClient == pstsdk::disk::database_ost);
pstsdk::byte get_crypt_method()
   return m_header.bCryptMethod;

Using that as a base, I could then check for ANSI/UNICODE, as well as OST/PST, and get the encryption method the store used:

bool ost = false;
bool ansi = false;
   database_check<ulong> db(argv[1]);
   ost = db.is_ost();
   ansi = true;
     database_check<ulonglong> db(argv[1]);
     ost = db.is_ost();
     ansi = false;
     wcout << L"Not a PST/OST store\n";
     return 0;



Aug 9, 2010 at 4:06 PM

One other approach if you only wanted to check ost/pst/ansi/unicode is to interpret the file (starting at byte 0) as a disk::header<ulong> and read the wVer and wVerClient fields directly. Not coincidentally, these fields are located at the same physical offsets in both ansi and unicode files.

Aug 9, 2010 at 4:07 PM

Btw ekidd, everything looks good in both patches now.