I seem to be “Riffing on Raymond” more and more these days, I’m not sure why, but..
Raymond Chen’s post today on the type model for Win64 got me to thinking about one comment he made in particular:
Notice that in these inter-process communication scenarios, we don’t have to worry as much about the effect of a changed pointer size. Nobody in their right mind would transfer a pointer across processes: Separate address spaces mean that the pointer value is useless in any process other than the one that generated it, so why share it?
Actually, there IS a really good reason for sharing handles across processes. And the Win64 team realized that and built it into the product (both the base team and the RPC team). Sometimes you want to allocate a handle in one process, but use that handle in another. The most common case where this occurs is inheritance – when you allocate an inheritable handle in one process, then spawn a child process, that handle is created in the child process as well. So if a WIn64 process spawns a Win32 process, all the inheritable handles in the Win64 process will be duplicated into the Win32 process.
In addition, there are sometimes reasons why you’d want to duplicate a handle from your process into another process. This is why the DuplicateHandle API has an hTargetProcessHandle parameter. One example of this is if you want to use a shared memory region between two processes. One way of doing this would be to use a named shared memory region, and have the client open it. But another is to have one process open the shared memory region, duplicate the handle to the shared memory region into the other process, then tell the other process about the new handle.
In both of these cases (inheritable handles and DuplicateHandle), if the source process is a 64bit process and the target process is a 32bit process, then the resulting handle is appropriately sized to work in the 32bit process (the reverse also holds, of course)
So we’ve established that there might be a reason to move a handle from one process to another. And now, the RPC team’s part of the solution comes into play.
RPC (and by proxy DCOM) defines a data type call __int32644. An int3264 is functionally equivalent to the Win32 DWORD_PTR (and, in fact, the DWORD_PTR type is declared as an __int3264 when compiled for MIDL).
An __int3264 value is an integer that’s large enough to hold a pointer on the current platform. For Win32, it’s a 32 bit value, for Win64, it’s a 64 bit value. When you pass an __int3264 value from one process to another it either gets truncated or extended (either signed or unsigned)..
__int3264 values are passed on the wire as 32bit quantities (for backwards compatibility reasons).
So you can allocate a block of shared memory in one process, force dup the handle into another process, and return that new handle to the client in an RPC call. And it all happens automagically.
Btw, one caveat: In the current platform SDK, the HANDLE_PTR type is NOT RPC’able across byte sizes – it’s a 32bit value on 32bit platforms and a 64bit value on 64bit platforms, and it does NOT change size (like DWORD_PTR values do). The SDK documentation on process interoperability is mostly correct, but somewhat misleading in this aspect. It says “The 64-bit HANDLE_PTR is 64 bytes on the wire (not truncated) and thus does not need mapping” – I’m not going to discuss the “64 bytes on the wire” part, but most importantly it doesn’t indicate that the 32-bit HANDLE_PTR is 32 bits on the wire.
Edit: Removed HTML error that was disabling comments…