如何捕获应用程序标题栏更改?

时间:2011-04-01 00:11:14

标签: vb.net winapi

我们正在Windows客户端平台(通常为WinXP)上运行小众行业计划,该计划在640x480窗口中运行回AS / 400服务器。为了减少我希望在程序的标题栏发生变化时要注意的错误。然后我需要捕获键盘条目以进行验证。然后我会确保每个条目都有效,因为古老的程序没有验证。然后,我可以弹出窗口,然后警告最终用户是否发生错误,并减少/消除异常报告。

我的问题是如何捕获我需要的应用程序标题栏change ='string'的事件? API调用?旨在在VB中执行此操作,除非另一个更明显更清洁。

2 个答案:

答案 0 :(得分:1)

我假设您不拥有目标应用程序的代码。在这种情况下,没有简单的“当标题改变时给我回电话”事件。然后,您有两个选项可以执行您需要的操作,我将在下面概述。

容易但不透气

让你的应用程序获得目标应用程序的主窗口(这应该很容易)并且每100毫秒左右轮询一次它的标题。当您检测到更改时,请采取相应措施。

困难但正确

使用例如Hook进入目标应用程序全球CBT hook。代码在其进程subclass their main window中运行后,会导致所有窗口消息首先通过您的代码。当您的代码看到WM_SETTEXT消息进入主窗口时,您可以使用IPC选择主动通知您的“其他”应用程序。如果您只需要喊“嘿!”对于您的其他应用程序,请使用auto-reset event(这将是最简单的)。当然,所有这些都非常重视非托管代码。

如果简单的解决方案不够好而且困难的解决方案太多,你可以尝试使用像White这样的自动化库(我从来没有使用它,所以我不能说更多)。

答案 1 :(得分:1)

WinEvents应该在这里运作良好。这些是在发生某些UI更改时被触发的轻量级事件 - 例如,对象的名称发生变化 - 其中包括标题栏文本更改。这种类型的钩子的一个好处是,您可以将其设置为将通知发送回您自己的进程,因此您不需要处理挂钩或IPC。 (它也适用于32位和64位进程。)

这在普通的C / C ++中最容易做到;但如果添加适当的[DllImport],可以在.Net(VB,C#)中完成。

#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;
}

需要注意的事项:你可能会得到假阳性;如果名称快速变化,当您获得第一个事件时,名称可能是第二个值,因此您可能会看到第二个值的两个事件。但是,如果您只是将其用作检查指定值的触发器,则这些都不是问题。