Как отловить изменение заголовка приложения? - PullRequest
0 голосов
/ 01 апреля 2011

Мы работаем на клиентской платформе Windows (обычно WinXP) в нишевой отраслевой программе, которая запускается в окне 640x480 обратно на сервер AS / 400. Чтобы уменьшить количество ошибок, я хочу следить за изменением строки заголовка программы. Затем мне нужно захватить записи клавиатуры для проверки. Затем я проверю правильность каждой записи, поскольку архаичная программа не проверяет. Затем я могу сделать всплывающее окно и предупредить конечного пользователя в случае возникновения ошибок, а также уменьшить / исключить отчеты об исключениях.

У меня вопрос: как мне записать событие нужной мне строки заголовка приложения = «строка»? Вызов API? Стремление сделать это в VB, если другой не будет заметным уборщиком.

Ответы [ 2 ]

1 голос
/ 02 апреля 2011

WinEvents здесь должно хорошо работать. Это легкие события, которые запускаются при определенных изменениях пользовательского интерфейса, например, при изменении имен объектов, включая изменения текста в Titlebar. Одним из преимуществ этого типа перехвата является то, что вы можете настроить его для доставки уведомлений обратно в ваш собственный процесс, поэтому вам не нужно заниматься перехватом или IPC. (Он также работает как с 32-битными, так и с 64-битными процессами.)

Это проще всего сделать на простом C / C ++; но может быть сделано в .Net (VB, C #), если вы добавите соответствующие [DllImport].

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

На что нужно обратить внимание: вы можете получить ложные срабатывания; и если имя быстро меняется, к тому времени, когда вы получите первое событие, имя может иметь второе значение, поэтому вы можете увидеть два события для второго значения. Однако ни один из них не должен вызывать проблем, если вы просто используете его в качестве триггера для проверки указанного значения.

1 голос
/ 02 апреля 2011

Я предполагаю, что вы не являетесь владельцем кода для целевого приложения. В этом случае нет простого события «Перезвони мне, когда название изменится». Затем у вас есть 2 варианта сделать то, что вам нужно, о чем я расскажу ниже.

Легко, но не герметично

Пусть ваше приложение получит главное окно целевого приложения (это должно быть достаточно просто) и будет опрашивать его заголовок каждые 100 мс или около того. Когда вы обнаружите изменение, действуйте соответственно.

Сложно, но правильно

Подключитесь к целевому приложению, например, с помощью глобальный CBT крюк . Как только ваш код выполняется в их процессе, подклассирует их главное окно , в результате чего все оконные сообщения сначала проходят через ваш код. Когда ваш код видит сообщение WM_SETTEXT, идущее в главное окно, вы можете сразу же уведомить ваше «другое» приложение, выбрав IPC . Если все, что вам нужно сделать, это просто крикнуть "эй!" для вашего другого приложения сделайте это с событием автосброса (это будет проще всего). Конечно, все это в значительной степени указывает на неуправляемый код.

Если простое решение недостаточно хорошее, а сложное слишком много, вы можете попробовать использовать библиотеку автоматизации, например White (я никогда не использовал ее, поэтому не могу сказать больше чем это).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...