С++ Изменение процедуры окна HWND во время выполнения

Я работаю в среде IDE, которая создает hwnd и соответствующие WndProc LRESULT CALLBACK. Мне нужно изменить WndProc на пользовательский.

Я читал, что SetWindowLong сделает эту работу, но я не могу найти ни одного работающего примера. Например:

HWND hwnd; //My window

SetWindowLong(hwnd, GWL_WNDPROC, myNewWndProc);

Третий параметр для SetWindowLong — это Long, как его называет имя функции. Как я могу сделать ссылку из моей функции WndProc на Long?

My WndProc:

LRESULT CALLBACK WndProcedure(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){

msg_dev(toString(uMsg));

switch(uMsg){

    case WM_MOUSEMOVE:
        SetCursor(LoadCursor(NULL, IDC_HAND));
        break;

    case WM_LBUTTONDOWN:
        msg_dev("Button down!");
        break;

    default:
        DefWindowProc(hwnd, uMsg, wParam, lParam);
}

return 0;
};

person ProtectedVoid    schedule 27.07.2015    source источник
comment
Вы говорите о подклассах. Используйте для этого SetWindowSubclass.   -  person Jonathan Potter    schedule 27.07.2015


Ответы (5)


Вам нужно использовать что-то вроде этого:

WNDPROC prevWndProc;
...
prevWndProc = (WNDPROC) SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG_PTR)&myNewWndProc);
...    
LRESULT CALLBACK myNewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    msg_dev(toString(uMsg));

    switch(uMsg)
    {
        case WM_MOUSEMOVE:
            SetCursor(LoadCursor(NULL, IDC_HAND));
            break;

        case WM_LBUTTONDOWN:
            msg_dev("Button down!");
            break;
    }

    return CallWindowProc(prevWndProc, hwnd, uMsg, wParam, lParam);
}

Смотрите эту статью:

Когда вы подклассифицируете окно, это исходная оконная процедура окна, к которому вы подклассируете, вы должны вызывать, когда вы хотите вызвать исходную оконную процедуру

При этом вы должны использовать SetWindowSubclass() вместо SetWindowLongPtr(). Смотрите эту статью:

Безопасное создание подклассов

Например:

SetWindowSubclass(hwnd, &mySubClassProc, 1, 0);
...    
LRESULT CALLBACK mySubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    msg_dev(toString(uMsg));

    switch(uMsg)
    {
        case WM_MOUSEMOVE:
            SetCursor(LoadCursor(NULL, IDC_HAND));
            break;

        case WM_LBUTTONDOWN:
            msg_dev("Button down!");
            break;

        case WM_NCDESTROY:
            RemoveWindowSubclass(hWnd, &mySubClassProc, 1);
            break;
    }

    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
person Remy Lebeau    schedule 27.07.2015
comment
Я получаю сообщение об ошибке при попытке использовать ваш новый стиль, мне сообщают об ошибках внешних символов для работы с defsubclassproc и setwindowsubclass , а также я не могу использовать свой подкласс в качестве параметра в setWindowSubClass, поскольку он говорит, что он несовместим с типом pfnsubclass - person Daniel Peedah; 28.05.2019
comment
@DanielPeedah Я получаю сообщение об ошибке при попытке использовать ваш новый стиль - у меня все работает. Мне выдаются ошибки внешнего символа - это ошибки компоновщика, а не компилятора. Вы ссылаетесь на comctl32.lib? Я не могу использовать свой подкласс в качестве параметра в setWindowSubClass, так как он говорит, что он несовместим - тогда ваша процедура объявлена ​​неправильно. Процедура, используемая с SetWindowSubclass(), имеет другие параметры, чем процедура, используемая с GWL_WNDPROC. Прочтите документацию, на которую я ссылался в своем ответе, и обратите особое внимание на примеры, показанные в моем ответе. - person Remy Lebeau; 28.05.2019
comment
Это связано с тем, что я создаю проект с помощью набора инструментов VS13? Я предполагаю, что именно поэтому я получаю ошибки символов для setWindowSubClass и DefSubClassProc. Поскольку intellisense не дает мне никаких ошибок до времени выполнения, это мое лучшее предположение. - person Daniel Peedah; 29.05.2019

Простой гипс делает свое дело.

SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG_PTR)&myNewWndProc);

В противном случае это были бы несовместимые типы: LRESULT и LONG.

person ProtectedVoid    schedule 27.07.2015

В документации MSDN для SetWindowLong() указано, что GWL_WNDPROC

Устанавливает новый адрес оконной процедуры.

Это означает, что ваш третий параметр должен быть указателем на функцию. Таким образом, ваш вызов SetWindowLong() должен выглядеть так:

SetWindowLong(hwnd, GWL_WNDPROC, (LONG_PTR)&myNewWndProc);

Обратите также внимание на раздел «Примечания», в котором говорится:

Приложение должно передавать любые сообщения, не обработанные новой оконной процедурой, в предыдущую оконную процедуру, вызывая CallWindowProc.

person user3347392    schedule 27.07.2015
comment
Требуется ли приведение, как показано в ответе @ProtectedVoid? - person user3347392; 28.07.2015
comment
Ну да, это так. Но это только часть истории. Реми хорошо освещает проблему. У вас есть большой опыт создания подклассов окон? - person David Heffernan; 28.07.2015
comment
Я добавил в свой ответ '(LONG_PTR)'. Подклассы окон - это не то, с чем у меня большой опыт. Я считаю, что этот ответ теперь полностью отвечает на исходный вопрос, но ответ @Remy, вероятно, более полезен. - person user3347392; 28.07.2015
comment
setWindowLong не требует LONG_PTR, он занимает LONG. setWindowLong(hwnd,GWL_WNDPROC,(LONG)newWindowProcedure); - person ; 18.07.2019

Вы можете использовать setWindowLong для решения вашей проблемы.

setWindowLong(hwnd,GWL_WNDPROC,(LONG)newWindowProcedure);

Однако вы будете устанавливать оконную процедуру дважды. Один раз с IDE по умолчанию, а затем с вашим. Что вам нужно сделать, это установить оконную процедуру, когда окно РЕГИСТРИРУЕТСЯ.

#include <windows.h>


void registerWindow();
void createWindow();
void messageLoop();


int main()
{
 registerWindow();
 createWindow();
 messageLoop();
}


LRESULT CALLBACK myWindowProcedure(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)
{
 return DefWindowProc(hwnd,msg,wparam,lparam);
}

void registerWindow()
{
 /** This is the important part.
    * Find this part in your code.
    * Set WNDCLASS::lpfnWndProc to what ever 
    * window procedure you want.
 **/

 WNDCLASS wc = {};

 wc.lpfnWndProc   = myWindowProcedure;
 wc.hInstance     = hInstance;
 wc.lpszClassName = "CLASS NAME";

 RegisterClass(&wc);

 // WARNING: Your app will crash at runtime if the 
 // windows procedure is "NOT PROPER"
}

void createWindow()
{
 auto hwnd = CreateWindowEx(
    0,                              // Optional window styles.
    "CLASS NAME",                     // Window class
    "Learn to Program Windows",    // Window text
    WS_OVERLAPPEDWINDOW,            // Window style

    // Size and position
    CW_USEDEFAULT, 
    CW_USEDEFAULT, 
    CW_USEDEFAULT, 
    CW_USEDEFAULT,

    NULL,       // Parent window    
    NULL,       // Menu
    HINSTANCE(),  // Instance handle
    NULL        // Additional application data
    );

   ShowWindow(hwnd, nCmdShow
}

void messageLoop()
{
    MSG msg;
    while( GetMessage(&msg, NULL, 0, 0) )
   {
    TranslateMessage(&msg); 
    DispatchMessage(&msg);
   }
}
person Community    schedule 18.07.2019

Вы должны использовать SetWindowLongPtr (который в 32-разрядной версии является макросом, а в 64-разрядной — отдельной функцией), чтобы обеспечить совместимость как с 32-, так и с 64-разрядными системами.

Синтаксис будет следующим:

SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)&myNewWndProc);

Примечание. Вместо SetWindowLong используется SetWindowLongPtr, а в качестве константы nIndex используется GWLP_WNDPROC.

person William    schedule 01.01.2020