Менеджер по обработке окон - PullRequest
       4

Менеджер по обработке окон

2 голосов
/ 25 февраля 2010

У меня есть Windows-бокс, на котором запущены три приложения. Когда приложения запускаются, каждое приложение создает окно без полей, которое расположено так, что они перекрываются определенным образом.

В данный момент, когда я нажимаю на элемент управления в нижнем окне, он достигает верхней части стека окон.

Мне нужно убедиться, что каждое окно сохраняет свой порядок в стеке, даже если окно получает входные данные.

Я думаю, мне нужно написать какой-нибудь простой оконный менеджер, который будет поддерживать правильный Z-порядок окон.

Проблема в том, что мне нужно знать, изменилось ли конкретное окно. Я обнаружил, что есть сообщение WM_WINDOWPOSCHANGING, но я понимаю, что это сообщение отправляется в окно, позиция которого изменилась.

Мне нужно, чтобы приложение моего оконного менеджера было как-то уведомлено об изменении Z-порядка.

Можно ли как-нибудь перехватить все сообщения WM_ и определить, относится ли это сообщение к одному из окон, которыми я хочу управлять?

Ответы [ 5 ]

2 голосов
/ 28 февраля 2010

Вместо подхода MSalter, пытающегося внедрить DLL в каждое из запущенных приложений, рассмотрите возможность установки хука Windows WH_CBT. В вашем CBTProc верните 0, когда вы получите HCBT_MOVESIZE для трех дескрипторов окна приложения, о которых вы заботитесь.

Считайте MSDN для документов на CBTProc и SetWindowsHookEx .

0 голосов
/ 29 апреля 2010

Думаю, я согласен с ответом Джона Кноллера. Если вы хотите, чтобы окна оставались в определенном порядке z, то определите, что это за порядок, и создайте окна с соответствующими отношениями родитель-потомок.

::SetWindowLong(hwnd_child, GWL_HWNDPARENT, hwnd_parent);

Когда вы это делаете, дочерние окна всегда остаются поверх родительских.

Если вы все еще настаиваете на перехвате сообщений, вы можете попробовать перехватить WM_ACTIVATE в каждом окне, а затем переслать это сообщение вашему оконному менеджеру, который будет иметь доступ к hwnds всех окон, и правильно их упорядочить по z, используя SetWindowPos. И вместо SetWindowPos вы можете использовать DeferWindowPos, чтобы изменить z-порядок сразу для окон и избежать мерцания.

0 голосов
/ 25 февраля 2010

Самый простой способ - внедрить DLL в каждое из трех приложений. Это гарантирует, что вам нужно иметь дело только с подмножеством оконных сообщений, которые вам действительно нужны.

Затем найдите главное окно в каждом приложении (не совсем тривиально, теоретически их может быть больше), вызвав EnumWindows(), чтобы найти все окна, и вызвав GetWindowThreadProcessId() для каждого, чтобы определить, принадлежит ли оно текущему процессу. (то есть тот, в который вставлена ​​ваша DLL).

Теперь, когда у вас есть правильный HWND, вы можете перехватить связанный WndProc и перехватить любой WM_WINDOWPOSCHANGING, отправленный на него.

0 голосов
/ 28 февраля 2010

Когда вы создаете два окна, которые вы хотите видеть сверху, задайте для окна, которым вы хотите быть внизу, значение hWndParent, равное CreateWindow. Окна будут всегда выдвигать эти окна вперед, когда нижнее окно выдвигается вперед, так что они всегда остаются перед ним.

Итак, если ваше нижнее окно - окно 1. Сначала создайте его, затем при создании окон 2 и 3 укажите дескриптор окна 1 в качестве значения hWndParent. Оконный менеджер сделает все остальное.

0 голосов
/ 25 февраля 2010

Вы можете использовать SetWindowPos, чтобы расположить ваши окна в желаемом порядке Z. Я предлагаю вам перехватить сообщение WM_FOCUS (оно отправляется в ваше окно, когда оно получает фокус)

В вашей функции wndProc вы можете попробовать что-то вроде этого:

LRESULT wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
    // other stuff..

    switch (msg){
        case WM_FOCUS:
        {
            HWND firstWindow; // get the first window here
            HWND secondWindow; // this would be the second window
            HWND thirdWindow; // this would be the third window
            // TODO: initialize the windows correctly, based on your priority
            SetWindowPos(firstWindow, secondWindow, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE); // position the second window below the first window
            SetWindowPos(secondWindow, thirdWindow, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE); // position the third window below the second window
        }
        return 0;
    }
    // other messages..
}

Я не совсем уверен в порядке следования аргументов SetWindowPos, так как сейчас не могу проверить код, но, возможно, это поможет вам?


Если вам нужно перехватить все сообщения WM_, я бы предложил класс Window, который приложения вместо этого (как я предполагаю) вызывают (* я полагаю) для вызова CreateWindowEx сами. Например:

class Window {
public
    Window(){
        ...
        WNDCLASSEX wc;
        ZeroMemory(&wc, sizeof(WNDCLASSEX));
        wc.cbSize        = sizeof(WNDCLASSEX);
        wc.lpfnWndProc   = wndProc;            // <- Note this one
        ...
    }

    static LRESULT WINAPI wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
        // reference: http://www.gamedev.net/community/forums/topic.asp?topic_id=303854 - Evil Steve  [Moderator]
        Window* parent;

        // Get pointer to window
        if(msg == WM_CREATE){
            parent = (Window*)((LPCREATESTRUCT)lParam)->lpCreateParams;
            SetWindowLongPtr(hwnd,GWL_USERDATA,(LONG_PTR)parent);
        }
        else{
            parent = (Window*)GetWindowLongPtr(hwnd,GWL_USERDATA);
            if(!parent) return DefWindowProc(hwnd,msg,wParam,lParam);
        }
        HWND prev = parent->mWin;
        parent->mWin = hwnd;
        LRESULT ret = parent->wndProc(msg,wParam,lParam);
        parent->mWin = prev;
        return ret;
    }

    virtual LRESULT wndProc(UINT msg, WPARAM wParam, LPARAM lParam){
    }
};

В этом примере ваши приложения будут наследоваться от Window, в основном предоставляя слегка измененную функцию wndProc (в ней будет отсутствовать HWND, поэтому ее нужно будет где-то хранить, если вы не заберете ее из Userdata).

  • Каждый раз, когда вы получаете сообщение, функция Window::wndProc(HWND, UINT, WPARAM, LPARAM) подхватывает его. Здесь вы можете проверять любые сообщения, включая (но не ограничиваясь) WM_WINDOWPOSCHANGING.

  • Другое, что нужно сделать, это:
    В wndProc(UINT, WPARAM, LPARAM) вместо звонка DefWindowProc(..)
    Вы звоните Window::wndProc(UINT, WPARAM, LPARAM). Тогда вы могли бы вместо этого делать там свои проверки (чтобы не забивать первую wndProc функцию) :)

Недостатком этого будет то, что если приложения написаны кем-то другим, они должны будут соответствовать вашему оконному классу. Когда вы объясняете это, пользователю не нужно взаимодействовать с вашим оконным менеджером, однако, при таком подходе единственное взаимодействие будет состоять в том, чтобы позволить вашему оконному менеджеру создать окно для пользователя.
В противном случае я думаю, что вам придется пойти с крючком, описанным в других ответах

...