Click here to Skip to main content
15,867,141 members
Articles / Programming Languages / C++
Article

Inside Windows Handles

Rate me:
Please Sign up or sign in to vote.
3.13/5 (14 votes)
19 Dec 20048 min read 130.7K   1.3K   31   6
This article is about some inner workings and the behavior of Windows handles.

Introduction

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.

Sharing Handles

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 to the source process
    HANDLE hSourceHandle,         // handle to duplicate
    HANDLE hTargetProcessHandle,  // handle to process to duplicate to
    LPHANDLE lpTargetHandle,      // pointer to duplicate handle
    DWORD dwDesiredAccess,        // access for duplicate handle
    BOOL bInheritHandle,          // handle inheritance flag
    DWORD dwOptions               // optional actions
);

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 TRUE for bInheritHandles parameter in the CreateProcesss (…) API for the child process to automatically inherit the handles from the parent process.

BOOL CreateProcess (
   PCTSTR pszApplicationName, 
   PTSTR pszCommandLine,
   PSECURITY_ATTRIBUTES psaProcess, 
   PSECURITY_ATTRIBUTES pszThread, 
   BOOL bInheritHandles, 
   DWORD dwCreationFlags, 
   PVOID pvEnvironment, 
   PCTSTR pszCurrentDirectory, 
   LPSTARTUPINFO pStartupInfo,
   PPROCESS_INFORMATION pProcessInformation
);

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 DuplicateHandle API.

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.

Appendix (A)

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 (
    DWORD dwFlags,
    DWORD th32ProcessID
);

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.

Appendix (B)

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 MessageBox or 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 SetWindowLong (…).

LONG SetWindowLong (
    HWND hWnd,       // handle of window
    int nIndex,      // offset of value to set
    LONG dwNewLong   // new value
);

where the nIndex value is GWL_WNDPROC and the dwNewLong is a AfxWndProc (..) global Afx API. So now, all messages go to AfxWndProc (..) and AfxWndProc(..) calls.

AfxCallWndProc (...) and, some where down the line, AfxCallWndProc (..) make a subsequent call to CallWindowProc (..) to let it handle unprocessed messages.

LRESULT CallWindowProc (
    WNDPROC lpPrevWndFunc,  // pointer to previous procedure
    HWND hWnd,              // handle to window
    UINT Msg,               // message
    WPARAM wParam,          // first message parameter
    LPARAM lParam           // second message parameter
);

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 PostThreadmessage (...):

MSG msg;
While (::GetMessage (&msg,0,0,0))
{
    if (msg.hWnd == NULL)
    {
        AfxCallWndProc (dlg,dlg->m_hWnd,WM_THRDMESSAGE,0,0);
    }
    DispatchMessage (&msg);
}

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.

Conclusion

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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralKernel Handles != User Handles - GDI Handles Pin
Thomas Weidenmueller19-Dec-04 23:49
Thomas Weidenmueller19-Dec-04 23:49 
Just to mention some facts:
- System Handles (such as files, sections, mutexes, semaphores, processes, threads, jobs, ...) are bound to a process and are only valid in it's scope. They may be inherited with CreateProcess() (depends on how the handles were created) to other processes or - as you mentioned - duplicated into the scope of another process. In kernel mode attaching to another process makes it's handle table available.
- User Handles (HWND, HICON/HCURSOR, HACCEL, ...) are global for all processes that share the same window station. They don't need to and can't be marshalled. However, system security may prevent access to objects belonging to threads of other desktops or when other restrictions are active (such as UI restrictions by jobs)
- GDI Handles are session-global. However, it's only possible to access global GDI handles, such as stock objects, and GDI objects created by the process itself. But the handles are unique, so it's impossible that there are same handles for different processes that are assigned to two different objects.
GeneralRe: Kernel Handles != User Handles - GDI Handles Pin
Peter Mares20-Dec-04 19:58
Peter Mares20-Dec-04 19:58 
GeneralRe: Kernel Handles != User Handles - GDI Handles Pin
Thomas Weidenmueller21-Dec-04 1:00
Thomas Weidenmueller21-Dec-04 1:00 
GeneralRe: Kernel Handles != User Handles - GDI Handles Pin
Peter Mares21-Dec-04 9:24
Peter Mares21-Dec-04 9:24 
GeneralRe: Kernel Handles != User Handles - GDI Handles Pin
Dr.Sai21-Dec-04 23:19
Dr.Sai21-Dec-04 23:19 
GeneralRe: Kernel Handles != User Handles - GDI Handles Pin
taraskin7-Jul-06 4:40
taraskin7-Jul-06 4:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.