When Charles Petzold told window handles are system wide handles, many might not have got the real implication behind his statement. What he meant was, Process-A can find out what is its window handle and some how send the handle value to Process-B, Process-B once it receives that handle can do a
SendMessage (...) or a
PostMessage (...) on that handle successfully. I demonstrate two techniques to do the same. One is using a file mapping and the other is more down-to-earth. Process-A gets a handle and writes it to a file, then Process-B can read that file, extract the handle, and do a
SendMessage (...) or a
PostMessage (...) successfully on that handle. This article discusses about how many ways are there to share handles, what it means to share a handle, and what all handles can be shared... Yes, not all handles can be shared.
Handles are 32 bit unsigned values that the Windows OS owns. It is usually the programmer who uses Windows native APIs to use these handles. This is one of the reasons why I am showing the second technique. If you were a nuts and bolts guy, you would want to see the handles being marshaled by your own eyes, and be sure that Windows does nothing on our backs. This doubt might arise when a beginner in Windows programming does a
FindWndow (…) and does a
SendMessage (...) or a
PostMessage (..) on the window handle returned by
FindWndow (..). There is a possibility that he or she might think that to get a handle of a window, we should request Windows OS and Windows OS is doing some marshalling on our behalf. This doubt should be eliminated by the time he or she completes reading this article. Some handles like handle to a thread or a handle to an Event object, their handle values cannot be written to files and given to other applications for use. (Here is where one would make use of a Windows native API to marshal them from one process to another process.) Some other handles, like handle to a device context (
HDC) cannot be sent directly or marshaled from one process to another even with the help of APIs. Among all the handles, window handles rein supreme. They have the power to be sent directly without any need for marshaling from one process to another without any use of Windows native APIs. That is why Charles Petzold calls them system wide.
Marshaling the handles
Let us see how these handles make sense when sent from one process to another process.
Thread handles and Event object handles, for instance, do not make sense in other processes when sent directly. We need to marshal them. We need to custom marshal them in particular. We will custom marshal some handles and write the marshaled handle value to a file/file mapping kernel object, and other processes can read from this file/ file mapping kernel object and operate on them.
BOOL DuplicateHandle (
HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions );
That's the API for custom marshalling a handle to a target process, it is the
lpTargetHandle variable that is written into a file or put in a file mapping kernel object.
Look at the
hTargetProcessHandle, yes you are right, this kind of duplication is per process duplication. To this end, I would also add that Thread handles of one thread do not make sense in a child process that the thread creates. Even there, you will have to duplicate the handle by using
DuplicateHandle (…) API.
Between a parent process and a child process, things get a little easier. You would need to pass
bInheritHandles parameter in the
CreateProcesss (…) API for the child process to automatically inherit the handles from the parent process.
BOOL CreateProcess (
Note: The thread IDs and the process IDs are system wide.
We cannot custom marshal all kinds of handles. For a complete list of handles that can be custom marshaled, click on this link.
About the project
SendWindowHandle project has it all. Just run it and it will run the ReceiveWindowHandle EXE. First, click on any button in the
SendWindowHandle dialog, and then click the corresponding button on the
ReceiveWindowHandle dialog to receive the handle either through a text file or a file-mapping object. This handle can be used in the ReceiveWindowHandle dialog application.
One argument one can give is Event objects already have a built in mechanism for sharing them across processes, then why do we need to do this
DuplicateHandle (...)? Yes, I would never ask you to do
DuplicateHandle on an event object, it is put here for the sake of some kind of completeness. But in case you are having an unnamed event object, you would definitely use the
On Some MFC Internals used in the project
It would be atrocious if I do not explain some of the Windows/MFC internals stuff used in the projects as they do occupy significant portion of the article. Beginners may find it useful. Allow me to cover them in the following appendices.
Towards the end in the SendWindowHandle project, I wrote a function
KillReceiveDialog (). It should be an interesting one. To start with, the SendWindowHandle EXE has the process ID of the
ReceiveWindowHandle. Using that process ID, it takes a snapshot of all the threads your system has, and when it locates the
ReceiveWindowHandle process, it posts a
WM_CLOSE message to that thread there by killing that thread, and therefore
ReceiveWindowHandle EXE exits.
HANDLE WINAPI CreateToolhelp32Snapshot (
This API takes a snapshot of the processes, and the heaps, modules, and threads used by the processes. See MSDN for a complete documentation. It is a very useful API to scan for system wide threads, processes and modules.
Note: Readers who do not like using CreateToolhelp32Snapshot can use their own ways of killing processes.
An insight into handling messages received from
PostThreadmessage (...) API:
There is one function in SendWindowHandle project
RunMesageLoop (...), the reason is as follows: messages sent by
PostThreadMessage (…) are not associated with a window. Messages that are not associated with a window cannot be dispatched by the
DispatchMessage function. Therefore, if the recipient thread is in a modal loop (as used by
DialogBox), the messages will be lost. To intercept thread messages while in a modal loop, use a thread-specific hook.
The above is straight out of Microsoft's web site.
Since I do not want to use a hook for this purpose, I do as shown below:
In the MFC architecture, the registered window procedure is always the
DefWindowProc (…). MFC traps when a window is created by installing a
WH_CBT hook, and replaces the window procedure by
LONG SetWindowLong (
HWND hWnd, int nIndex, LONG dwNewLong );
nIndex value is
GWL_WNDPROC and the
dwNewLong is a
AfxWndProc (..) global Afx API. So now, all messages go to
AfxWndProc (..) and
AfxCallWndProc (...) and, some where down the line,
AfxCallWndProc (..) make a subsequent call to
CallWindowProc (..) to let it handle unprocessed messages.
LRESULT CallWindowProc (
WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
lpPrevWndFunc is the
DefWindowProc(...), so no problem, everything works fine.
Since thread messages do not get passed on to their window procedures, the onus is on us to do the same. Okay, so I write something like this to get messages sent by
While (::GetMessage (&msg,0,0,0))
if (msg.hWnd == NULL)
But there is one problem here, I can't enter the message loop, it has already come into existence once
DoModal (…) is called.
Luckily, MFC implements modal and modeless dialogs in almost the same way. In both the cases, it calls
CreateDialogInderict (...), the Win32 API for creating modeless dialogs and implements modality by itself. So there is a message loop lurking around.
Still, I can't enter the message loop that is already running, and I do not want to use a hook, so I replace the original message loop itself. In the
InitDialog (..), I do a
PostMessage (WM_MESSAGE_LOOP) (
WM_MESSAGE_LOOP is a custom message) and that runs another message loop. See
RunMesageLoop (WPARAM wParam, LPARAM lParam) function . Now, the main message loop comes to a grinding halt and my custom message loop takes over and is ready to dispatch thread messages.
One thing to remember is once this message loop is terminated (when the user closes the dialog), the main message loop kicks in
OnCancel (..). I will have to kill that message loop and therefore I call
PostMessage (WM_CLOSE); in
OnCancel (), there by exiting the application very peacefully. So here is how we can custom marshal messages too.
While I do all this, this very problem was addressed by the Microsoft team, and Microsoft being Microsoft presents a totally different solution to the problem in its help and support web site. The reader can also look into an article by me in CodeGuru for a better understanding of the message maps in MFC.
Though this article is about some inner workings and the behavior of handles, let us just see why we need to share handles between processes. Usually, when the main application is made up of many small executables, these small executables might need to get some handles residing in the main executable to operate on them. Like, a main application might be just a user interface, and the dependant small executables might be the ones that do some background processing. There was a case when I had an executable running just to ensure it cleans up (temporary files) properly in case the main executable exited abruptly or crashed. Once I had an executable monitoring one thread of the application; if the thread crashes, it will bring down the application; then this executable will bring up the application altogether from scratch. Else, say a large amount of data has to be passed from one exe to another for processing of data. We need the unnamed file-mapping object handle pointing to the data to be sent across processes.