Inside Windows Handles[www.codeproject.com]

Inside Windows Handles
By Dr_Sai

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.

<script defer="defer" language="javascript" type="text/javascript">Highlight(Array());</script>

Dr_Sai


Click here to view Dr_Sai's online profile.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值