Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Modifying Popup Windows, Part 2


Modifying Popup Windows, Part 2

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, we’ll 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 haven’t run into too many cases where I’ve 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 it’s initialization of the window. We’d like to trap on an event that is sent just before the window appears rather than just after it’s created to ensure we have the greatest chance of getting the right caption.

Fortunately, we can use another of SetWindowsHookEx’s 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.

Let’s 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:

  1. Be sure you minimize the amount of code in your hook and do not use third-party frameworks such as MFC to save time writing the DLL. These items would have to be loaded into the process space of each application on the user’s desktop and could eat up unnecessary amounts of memory.
  2. Realize that once you terminate the hook, the DLL doesn't get dropped out of memory until the last application using it is terminated. This means that updating your hook DLL can be problematic if it's hooking widely used messages such as those we trapped above. You may not be able to update the DLL without a system reboot.
  3. Test, test, test, and then test again. A hook DLL is almost as tricky as working on a device driver. I've seen spectacular system crashes and lockups with hook DLL's that were improperly written even with newer operating systems like Windows 2000. Don't assume that just because it's a 32-bit OS, you can get away with minor failures. They could end up crashing or destablizing your user's system.
  4. If you're relying on a method like window captioning to determine identity, check to be sure that the text you are relying on is reliable and doesn't change from version to version of the underlying application. This could trip up a hook that needs to support multiple versions of applications.
  5. Another approach that I didn't explore but that may also be useful in some cases is to isolate the window by checking to see if it belongs to a particular EXE. To do this, you would call GetCurrentProcess() within the hook DLL (which will return the process handle of the application the hook DLL is running within) and then GetModuleFilenameEx() to get the filename of the process. You could then do a comparison to see if it's the one you want.

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].


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.