Simulating a drop, part two

Raymond Chen

Last time, we wrote a tiny program to simulate dropping a file on another file, but we discovered that it didn’t work for dropping a file onto Mail Recipient.MAPIMail. The reason, as you no doubt instantly recognized, is that the MAPIMail handler creates a worker thread, and we’re exiting the process before the worker thread has finished its work.

And as you no doubt know by now, the solution is to use the SHSetInstanceExplorer function. Let’s bring back the ProcessReference class and use it to solve our process lifetime problem.

int __cdecl wmain(int argc, WCHAR **argv)
{
 ProcessReference ref;

Compile this program and run it with the command line

fakedrop c:\autoexec.bat “%USERPROFILE%\SendTo\Mail Recipient.MAPIMail”

to watch our process reference save the day.

Oh wait, it didn’t help. What’s going on?

Run this under the debugger and you’ll see an interesting exception:

(918.110): Unknown exception – code 80010012 (first chance)

A little hunting through winerror.h reveals what this exception means:

//
// MessageId: RPC_E_SERVER_DIED_DNE
//
// MessageText:
//
//  The callee (server [not server application]) is not available and
//  disappeared; all connections are invalid. The call did not execute.
//
#define RPC_E_SERVER_DIED_DNE            _HRESULT_TYPEDEF_(0x80010012L)

Huh? What’s this RPC stuff? Oh wait, COM uses RPC. That should be a clue.

Notice that our ProcessReference‘s destructor runs after we have already uninitialized COM. When we invoked the IDropTarget::Drop method on the MAPIMail drop target, it kicked off a background thread to do the work, and in order to access the parameters from the background thread, it had to do a bit of marshalling, with the help of the functions with ridiculously long names CoMarshalInterThreadInterfaceInStream and the only slightly less ridiculous CoGetInterfaceAndReleaseStream. But since we tear down COM immediately, when the background thread gets around to asking, “Okay, and what was that file name?” the thread that has the answer (the main thread) has already shut down COM. Hence the “server died” error.

To fix this, we need to fix the scope of the process reference object:

 if (argc == 3 && SUCCEEDED(CoInitialize(NULL))) {
  ProcessReference ref;

Compile this program and run it with the same command line and… it still doesn’t work! But this time you definitely know why: The destructor is running at the wrong time.

I leave it as an exercise to fix the destructor timing problem. To see whether you got it right, run the fakedrop command line again. When you successfully get an email message, then you know you’ve got it.

0 comments

Discussion is closed.

Feedback usabilla icon