Я думаю, что попал в ту же ловушку, что и многие до меня, когда я пытаюсь применить хорошую методологию OO к программированию Win32 API. Ни MFC, ни AFX, я даже не использую VC++, я использую C::B с gcc.
Я думаю, что то, что я пытаюсь сделать, невозможно, но поскольку MFC существует (хотя я им не пользуюсь), должен быть какой-то способ.
Я создал класс, содержащий несколько оконных элементов управления. Он реализует обработчики для WM_CREATE и WM_COMMAND и отслеживает все связанные данные вокруг моей небольшой группы элементов управления (идентификационные коды и HWND).
Он прекрасно работает с кнопками, статическими элементами управления и даже легкими методами GDI, но все это ломается, когда я пытаюсь создать подкласс элемента управления редактирования.
На самом деле, я просто хочу захватить клавишу «ввод», но, как подтвердит любой, кто шел по этому пути раньше, когда элемент управления редактирования имеет фокус, родительское окно не получает WM_KEYDOWN или WM_COMMAND, нам остается реализовать свои собственные проц. Супер отстой.
Хорошо, так что создание подкласса элемента управления редактирования — это нормально, если editProc является глобальным или статическим. Я знаю, что это потому, что SetWindowLongPtr нужен адрес функции, а эта концепция туманна для функции-члена.
Итак, объект моего класса объявлен как «статический» внутри родительского WndProc. Но функция не является «статической», потому что тогда у меня не было бы доступа к нестатическим элементам данных (полностью лишающим цели этого упражнения). Я надеюсь, что, поскольку объект сам по себе статичен, я смогу правильно определить адрес одной из его функций-членов.
Читатели, которые пробовали это раньше, либо сдались и использовали MFC или что-то еще, либо, возможно, нашли умный обходной путь.
Я позволю этому примеру кода говорить об остальном: (упрощенный - не будет компилироваться как таковой)
/**** myprogram.c ****/
#include "MyControlGroup.h"
int winMain(){ // etc... }
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// object is static becuse it only needs to be initialized once
static MyControlGroup myControl;
if (msg == WM_CREATE)
myControl.onWMCreate(hWnd);
else if (msg == WM_COMMAND)
myControl.onWMCommand( wParam, lParam );
else if (msg == WM_DESTROY)
PostQuitMessage(0);
return DefWindowProcW(l_hWnd, l_msg, l_wParam, l_lParam);
}
Заголовочный файл для моего класса:
/**** MyControlGroup.h ****/
class MyControlGroup
{
private:
HWND m_hWndParent;
HWND m_hWndEditBox;
int m_editBoxID;
public:
MyControlGroup();
void onWMCreate(HWND);
void onWMCommand(WPARAM, LPARAM);
// want to find a way to pass the address of this function to SetWindowLongPtr
LRESULT myEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
};
... и реализация:
/**** MyControlGroup.cpp ****/
static int staticID = 1;
MyControlGroup::MyControlGroup()
{
m_editBoxID = staticID++;
}
void MyControlGroup::onWMCreate(HWND hWnd)
{
// My control group has buttons, static controls, and other stuff which are created here with CreateWindowW. It also has an edit control:
m_hWndEditBox = CreateWindowW(L"EDIT", L"initial text", WS_CHILD | WS_VISIBLE | WS_BORDER, 10, 10, 150, 20, hWnd, (HMENU)m_editBoxID, NULL, NULL);
/*
To subclass the edit control, I need a pointer to my customized proc. That means I
need a pointer-to-member-function, but SetWindowLongPtr needs a pointer to global or
static function (__stdcall or CALLBACK, but not __thiscall).
*/
// I'd like to do something like this, adapted from a great write-up at
// http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
LERSULT (MyControlGroup::*myEditProcPtr)(HWND, UINT, WPARAM, LPARAM);
myEditProcPtr = &MyControlGroup::myEditProc;
// Up to now it compiles ok, but then when I try to pass it to SetWindowLongPtr, I get
// an "invalid cast" error. Any ideas?
SetWindowLongPtr(m_hWndEditBox, GWLP_WNDPROC, (LPARAM)myEditProcPtr);
}
void MyControlGroup::onWMCommand(WPARAM wParam, LPARAM lParam){ /* process parent window messages. Editboxes don't generate WM_COMMAND or WM_KEYDOWN in the parent :''( */}
LRESULT MyControlGroup::myEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// process messages like IDOK, WM_KEYDOWN and so on in the edit control
}
Даже после того, как я это сделаю, мне все равно нужно будет найти способ передать адрес родительского WndProc в myEditProc для возвращаемого значения, но пока я не преодолею это, нет смысла беспокоиться об этом.
Заранее спасибо за чтение!
SetWindowSubclass
, если можете... - person K-ballo   schedule 07.01.2013