CoGetServerPID

Kim Gräsman, October 2003

Background

Marion Nalepa once said:

It would seem that if I create the Word instance with CreateObject, I get the automation reference but not the process id. If I create it with OpenProcess I get the reverse. How do I end up with both?

This got me curious. What if you could query any interface pointer for the PID of the process in which its implementation is hosted?

Detective work

I figured the serialized form of an interface pointer (called an OBJREF) should contain the source process ID, so I turned to the DCOM specification for help. I didn't really find anything indicating that the OBJREF did indeed contain the source PID, but I figured if I could debug a single interface marshalling where I knew the process ID, I could easily find it within the OBJREF.

Conveniently enough, Dharma Shukla had already built a little function called ShowMeMyOBJREF, which dumps all discernable information about an OBJREF, so I built a little code snippet that instantiated Word.Application, marshalled it to a stream, which I passed to Dharma's ShowMeMyOBJREF. It didn't take me much more than 5 minutes to compare the WINWORD.EXE PID from Task Manager to the OBJREF struct in the debugger, and find it.

It turns out the process ID is encoded as part of the IPID, which is defined in the DCOM spec like so:

An IPID is a 128-bit identifier known as an interface pointer identifier which represents a particular interface on a particular object in a particular server. As it is passed in the object ID fields of a DCE RPC, the static type of an IPID is in fact a UUID. However, IPIDs are scoped not globally but rather only relative to the server process which originally allocated them; IPIDs do not necessarily use the standard UUID allocation algorithm, but rather may use a machine-specific algorithm which can assist with dispatching.

As the specification says, the IPID is transmitted as a GUID, but it's not necessarily generated using CoCreateGuid or similar. Rather, the second sequence (Data2 member of the GUID struct) actually contains the source PID.

Implementation

So, borrowing some ideas from the ShowMeMyOBJREF source, I built CoServerGetPID, which takes an IUnknown pointer and basically does this:

See the accompanying source code for details.

Marshal-by-value is a typical example of where this technique would fail. The function should return E_NOINTERFACE for custom-marshalled objects, and RPC_E_INVALID_OBJREF for any interface where it can't resolve the process ID.

Disclaimer

First off, I've tried this technique for in-process and out-of-process servers, but not yet for remote servers. It may work, but it is of limited benefit, since all you'd get would be the remote PID, which you can't really use for anything on the local machine. Feel free to experiment, though.

Second, please note that this is largely a product of my curiosity. It may not be a good idea to use this technique in production systems, since it relies on undocumented aspects of the DCOM/RPC marshalling layer. On the other hand, in the year 2003, I don't find it very likely that Microsoft would tear up the DCOM wire representation and re-design it, so if you test this code and it works, you're probably pretty safe using it.

But don't tell anyone I told you that, and please consider the risks before using it. You don't need me to tell you that I will not be held responsible if your code fails on Longhorn, Wine, etc.

Code

I made the little routine available for download as a single header file. I've actually only tested this on Windows XP, so I'd love to hear about compatibility issues on other platforms.

The header relies on ATL for automatic resource management of interface pointers, so it includes atlbase.h, and will not compile with a pure C compiler. If you need that, just tweak the code yourself.

cogetserverpid.h
uCoGetServerPID.pasDelphi port contributed by Jon Robertson — thanks!

Please contact me if you see room for improvement in text or code (Note: this concerns the C++ version — Jon's Delphi stuff I know nothing about).

Revision History

2004-11-22: Alexander Nickolov provided a well-needed code review: