Detect the creation of third-party popup windows by looking at the window caption
December 09, 2002
URL:http://drdobbs.com/modifying-popup-windows-part-2/184416791
I need to know when a particular popup window appears from a third-party application so that I can modify its appearance. How can I do this?
In the last newsletter, we explored how to know when a window appears based on its window class. We explored the use of a window hook, installing it, removing it, and testing values provided by Windows during an event trap.
This issue, well continue looking at hooking by examining how we might do this by looking at the window caption instead of the window class.
Currently, unique window classes are becoming much scarcer, and I havent run into too many cases where Ive been able to identify third-party windows by their classes. Most often, I fire up Spy++, click on the window I want to look at, access the window properties and see the class name as #32770. Bummer. This is the class name used by an MFC window. MFC creates window classes for various default window styles as a convenience to the developer. One of the advantages of using a framework is just such assistance with hiding the grunge of Windows. But it makes it impossible to distinguish one MFC window from another on the basis of the window class.
So what to do? Another technique is to use the window caption. The window caption for a window can be easily obtained by calling GetWindowText() with the proper HWND. Again, the trick is to know when these windows come into existence so we can determine if the particular window is the one that we want.
Last time we explored use of SetWindowsHookEx() with the WH_CBT style to trap on window creation events. At first glance, this seems like it might be the way to go. We get the CREATESTRUCT values passed to a call to CreateWindow() just after the window was created. However, there is a drawback. There is no way to guarantee that the lpszName attribute of the structure will be set to the correct window caption. The application may override the caption by a later call to SetWindowText() during its initialization of the window. Wed like to trap on an event that is sent just before the window appears rather than just after its created to ensure we have the greatest chance of getting the right caption.
Fortunately, we can use another of SetWindowsHookExs styles called WH_CALLWNDPROC. This style allows us to handle messages just prior to the time they are dispatched to the window they are intended for. In our case, the message we want to look at is WM_ACTIVATE. This message is sent to a window as it is being activated and deactivated. At this point in the creation cycle we can be pretty sure that the window is ready for display and the underlying application has finished initializing it.
Lets look at some code. First, we create the boilerplate methods we need to install the hook (for more info on why this needs to be in a DLL, see the prior issue of the newsletter):
static HINSTANCE g_hModule = NULL; BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: g_hModule = (HINSTANCE)hModule; break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } static HHOOK g_hHook = NULL; __declspec(dllexport) void InitHook() { g_hHook = SetWindowsHookEx( WH_CALLWNDPROC,(HOOKPROC)OnHookProc,g_hModule,0 ); if ( g_hHook ) MessageBox( NULL,"hook created","",MB_OK ); else MessageBox( NULL,"hook was not created","",MB_OK ); } __declspec(dllexport) void TermHook() { if ( g_hHook ) UnhookWindowsHookEx( g_hHook ); g_hHook = NULL; }
Pretty straightforward, especially if you read through the last issue of the newsletter. We install the hook in InitHook() and tear it down in TermHook(). Again, these messages would be called in your application's initialization and termination code, respectively.
The next step is to create the hook function itself called OnHookProc():
static LRESULT OnHookProc(int nCode,WPARAM w,LPARAM l) { LRESULT lr = CallNextHookEx(g_hHook,nCode,w,l); if ( nCode == HC_ACTION ) { CWPSTRUCT* p = (CWPSTRUCT*)l; if ( p && (p->message == WM_ACTIVATE && p->wParam==WA_ACTIVE) ) { char caption[1024]; ZeroMemory(caption,sizeof(caption)); SendMessage( p->hwnd,WM_GETTEXT,sizeof(caption)-1,(LPARAM)caption ); if ( !strcmpi(caption,"MyCoolWindowCaption") ) { ::MessageBox( NULL,found it,,MB_OK ); } } } return lr; }
Notice that we call the function CallNextHookEx() here before we process the message. In our case, we don't care about changing the way the message is processed, we just want to know that it is being processed. So we give the default window proc a chance to do its thing before we do ours. This also ensures that if the underlying app messes with the caption in WM_ACTIVATE, we get the mod rather than the original value.
The code is fairly straightforward. We are looking for a window that becomes active by checking the message type and whether it is going active or inactive. Then, it's a simple call to SendMessage() with WM_GETTEXT to get the window caption for the window, and then finally our comparison test.
Some other words of advice about using hooks:
Feel free to send me questions you may have that might be of interest to other developers and we'll explore them together in a future issue of the Windows Q&A newsletter.
Mark M. Baker is the Chief of Research & Development at BNA Software located in Washington, D.C. Send your Windows development questions to [email protected].
Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.