开发者

How to catch application titlebar change?

We are running on a Windows Client Platform (generally WinXP) in niche industry program that runs in a 640x480 window back to an AS/400 server. To reduce errors I want to watch for when the title bar of the program changes. Then I need to capture the keyboard entries to validate. I'll then make sure each of the entries is valid since the archaic program does no validation. I'll can then do a pop-up then warning the end-user if errors occur an开发者_Python百科d to reduce/eliminate the exception reports.

My question is how can I capture the event of the application title bar change = 'string' that I need? API call? Aiming to do this in VB unless another would be notable cleaner.


WinEvents should work well here. These are lightweight events that get fired when certain UI changes take place - eg names of objects change - which includes Titlebar text changes. One benefit to this type of hook is that you can set it up to deliver the notifications back to your own process, so you don't need to deal with hooking or IPC. (It also works against both 32-bit and 64-bit processes.)

This is easiest to do in plain C/C++; but can be done in .Net (VB, C#) if you add the appropriate [DllImport]'s.

#include <windows.h>
#include <stdio.h>

#define WM_NAMECHANGED WM_APP

HWND g_hwndTarget; // window we're listening to

void CALLBACK WinEventProc(
    HWINEVENTHOOK hWinEventHook,
    DWORD event,
    HWND hwnd,
    LONG idObject,
    LONG idChild,
    DWORD dwEventThread,
    DWORD dwmsEventTime
)
{
    // Check this is the window we want. Titlebar name changes result in these
    // two values (obtained by looking at some titlebar changes with the 
    // Accessible Event Watcher tool in the Windows SDK)
    if(hwnd == g_hwndTarget && idObject == OBJID_WINDOW && idChild == CHILDID_SELF)
    {
        // Do minimal work here, just hand off event to mainline.
        // If you do anything here that has a message loop - eg display a dialog or
        // messagebox, you can get reentrancy.
        PostThreadMessage(GetCurrentThreadId(), WM_NAMECHANGED, 0, 0);
    }
    return;
}

void ReportName(HWND hwnd)
{
    WCHAR szName[128];
    GetWindowText(hwnd, szName, ARRAYSIZE(szName));
    wprintf(L"hwnd 0x%08lx has title: %s\n", HandleToLong(hwnd), szName);
}

int main()
{
    wprintf(L"Place mouse pointer over window titlebar to report name changes for and hit return...\n");
    getchar();
    POINT pt;
    GetCursorPos(&pt);
    g_hwndTarget = WindowFromPoint(pt);
    ReportName(g_hwndTarget);

    // Note: this doesn't work for console windows, which are managed by CSRSS.EXE. Simplest (though not efficient) workaround for those
    // is to use threadId=0 and filter by hwnd in the callback.
    DWORD threadId = GetWindowThreadProcessId(g_hwndTarget, NULL);

    // This says: call the callback when any UI elements in the specified thread change
    // name. _OUTOFCONTEXT means deliver the notifications in this process, don't hook.
    HWINEVENTHOOK  hook = SetWinEventHook(EVENT_OBJECT_NAMECHANGE, EVENT_OBJECT_NAMECHANGE, NULL, WinEventProc, 0, threadId, WINEVENT_OUTOFCONTEXT);
    // TODO: add error checking as appropriate.

    wprintf(L"Waiting...\n");

    // Thread needs to have a message loop for SetWinEventHook to work for out-of-context messages.
    UINT count = 10;
    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0))
    {
        if(msg.message == WM_NAMECHANGED)
        {
            ReportName(g_hwndTarget);
            if(--count == 0)
            {
                break;
            }
        }
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    UnhookWinEvent(hook);
    return 0;
}

Things to watch for: you might get false-positives; and if the name changes rapidly, by the time you get the first event, the name may be at the second value, so you may appear to see two events for the second value. Neither of these should be an issue if you're just using this as a trigger to check for a specified value, however.


I am assuming that you do not own the code for the target application. In this case, there's no easy "call me back when the title changes" event. You then have 2 options to do what you need, which I will outline below.

Easy but not airtight

Have your application get the main window of the target application (this should be easy enough) and poll its title every 100msec or so. When you detect a change, act accordingly.

Difficult but correct

Hook into the target application using e.g. a global CBT hook. Once your code runs in their process subclass their main window, causing all window messages to go through your code first. When your code sees a WM_SETTEXT message going to the main window, you can actively notify your "other" application on the spot using your choice of IPC. If all you need to do is just shout "hey!" to your other application, do so with an auto-reset event (it will be easiest). Of course all this points heavily to unmanaged code.

If the easy solution is not good enough and the difficult one is too much, you can try using an automation library like White (I 've never used it, so I can't really say more than that).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜