In order to send a message to another process, it is necessary to know the handle of the receiving window. However, if that process is created using CreateProcess or opened using OpenProcess, only the handles of the process and of it's main thread are known.
It is normally possible to find the window, if the title is known, simply by enumerating all windows and searching for the right window. When the name is unknown
however, it is a little more complex to get its handle.
In order to find that handle it is, however, possible to scan all top-level windows and compare each window process Id with the Id received from the CreateProcess or the OpenProcess function. Please take note that not all Windows messages can be sent between processes. It is why use WM_COPYDATA is recommended.
Finally, remember that the message will need to be sent to the Main Frame. If it is intended for a child window ( a CView-derived class, for example ), it should be sent to the Main Frame, and then redirected in the target application.
In a private communication with a Senior Support Engineer at Microsoft, it has been noted that there is no guarantee that a process has any windows at all. It is also
possible that a process has more than one window. Even if you get the correct window, it must know about and be able to handle the message being sent. A window always has a process, but a process can have zero to any number of windows, so there is no way to make a correlation between a process and a particular window.
I personally recommand a lot of care when using this method. However, this technique is working well in my projects, and I now use it for all inter-process communication.
Furthermore, since I have included my code in several different projects, I have found only two problems with this code:
- The Process must have finished it's initialization. This can be verified with the following call made before the call to SendMessage :
WaitForInputIdle( ProcessInfo.hProcess, INFINITE );
- The Process must be receiving its messages. In order to go around this problem, I place my calls to SendMessageToProcess in a while loop, which waits to the receiving application to send back a WM_COPYDATA message confirming reception.
To add a function that send a message to a Process, perfoms the steps listed bellow.
NOTE: These steps assume that the name of the CWinApp-derived object is CMyWinApp.
- Add the Following members to the declaration of CMyWinApp in MYWINAPP.H:
public:
void SendMessageToProcess(LPPROCESS_INFORMATION lpProcessInformation,
UINT Msg, WPARAM wParam, LPARAM lParam);
protected:
static BOOL CALLBACK EnumWindowCallBack(HWND hwnd, LPARAM lParam);
- Define the CMyWinApp::SendMessageToProcess function.
void CMyWinApp:: SendMessageToProcess(LPPROCESS_INFORMATION lpProcessInformation,
UINT Msg, WPARAM wParam, LPARAM lParam)
{
FINDWINDOWHANDLESTRUCT fwhs;
fwhs.ProcessInfo = lpProcessInformation;
fwhs.hWndFound = NULL;
EnumWindows ( EnumWindowCallBack, (LPARAM)&fwhs ) ;
SendMessage ( fwhs.hWndFound, Msg, wParam, lParam );
}
- Define the CMyWinApp::EnumWindowCallBack function.
BOOL CALLBACK CMyWinApp::EnumWindowCallBack(HWND hwnd, LPARAM lParam)
{
FINDWINDOWHANDLESTRUCT * pfwhs = (FINDWINDOWHANDLESTRUCT * )lParam;
DWORD ProcessId;
CString Title;
GetWindowThreadProcessId ( hwnd, &ProcessId );
CWnd::FromHandle( hwnd )->GetWindowText(Title);
if ( ProcessId == pfwhs->ProcessInfo->dwProcessId && Title.GetLength() != 0)
{
pfwhs->hWndFound = hwnd;
return false;
}
else
{
return true;
}
}