これまでは、独自にウィンドウプロシージャを定義し、そのウィンドウプロシージャを持つウィンドウクラスを独自に登録し、そのウィンドウを作成し、別のウィンドウプロシージャに差し替えを行った。
さて、スタティックコントロールや、エディットボックスの場合は、既定のウィンドウクラスがあるわけで、そのウィンドウクラスのウィンドウプロシージャを差し替えることも出来るだろうか?
結論としては出来る。
ただし、普通のウィンドウのように差し替えただけでは正しく動作しない場合がある。
CallWindowProc.cppで、
#include <windows.h>
#include <tchar.h>
#include <commctrl.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK EditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
WNDPROC DefEditProc;
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd){
HWND hWnd;
MSG msg;
WNDCLASS wc;
wc.lpszClassName = _T("CallWindowProc");
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
if (!RegisterClass(&wc)){
MessageBox(NULL, _T("RegisterClass Failure!"), _T("CallWindowProc"), MB_OK | MB_ICONHAND);
return -1;
}
hWnd = CreateWindow(_T("CallWindowProc"), _T("CallWindowProc"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if (hWnd == NULL){
MessageBox(NULL, _T("CreateWindow Failure!"), _T("CallWindowProc"), MB_OK | MB_ICONHAND);
return -2;
}
ShowWindow(hWnd, SW_SHOW);
while (GetMessage(&msg, NULL, 0, 0) > 0){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
エディットボックス用に新たに定義したEditProcを用意。
また、エディットボックス既定のウィンドウプロシージャを持っておくポインタDefEditProcも用意。
WindowProcは、
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
switch (uMsg){
case WM_CREATE:
{
LPCREATESTRUCT lpCreateStruct;
HWND hEdit = NULL;
lpCreateStruct = (LPCREATESTRUCT)lParam;
hEdit = CreateWindow(WC_EDIT, _T(""), WS_CHILD | WS_VISIBLE | WS_BORDER | WS_HSCROLL | WS_VSCROLL | ES_MULTILINE | ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 100, 100, 300, 300, hwnd, (HMENU)WM_APP + 1, lpCreateStruct->hInstance, NULL);
DefEditProc = (WNDPROC)GetWindowLong(hEdit, GWL_WNDPROC);
SetWindowLong(hEdit, GWL_WNDPROC, (LONG)EditProc);
}
break;
case WM_DESTROY:
{
PostQuitMessage(0);
}
break;
default:
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
WM_CREATEでエディットボックスを作成。
そして、もともとのウィンドウプロシージャのポインタはDefEditProcに格納しておき、EditProcを新たにセット。
EditProcは、
LRESULT CALLBACK EditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
switch (uMsg){
case WM_LBUTTONDOWN:
{
MessageBox(NULL, _T("WM_LBUTTONDOWN"), _T("CallWindowProc"), MB_OK);
}
break;
default:
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
マウス左クリックでメッセージボックスを出すようにしている。
起動時。
左クリックには対応している。
ただ、文字列など入力には対応できてない。
そこで、
CallWindowProcA function (winuser.h) - Win32 apps | Microsoft Docs
CallWindowProcを使って、もともとのエディットボックスのウィンドウプロシージャDefEditProcを呼び出す。
LRESULT CALLBACK EditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
switch (uMsg){
case WM_LBUTTONDOWN:
{
MessageBox(NULL, _T("WM_LBUTTONDOWN"), _T("CallWindowProc"), MB_OK);
}
break;
default:
break;
}
return CallWindowProc(DefEditProc, hwnd, uMsg, wParam, lParam);
}
こうすると、
さっきは反映されてなかったWS_BORDERが反映された。
クリックには対応してる。
キャレットも出ている。でも入力が反映されない。
MessageBoxが余計かな。
あー、入力出来た。
ここには来てるから、新たに定義したウィンドウプロシージャには来てるんだけどね。
あとは、ウィンドウメッセージに対する処理を書けばいいのだが、これだけでは都度都度上手くいかない場合(CallWindowProcで既定プロシージャを呼ぶ場合、DefWindowProcを呼ぶ場合、0などその場で値を返す場合など。)があるので、それはメッセージごとにいずれ・・・。
Sample/winapi/CallWindowProc/CallWindowProc/src/CallWindowProc at master · bg1bgst333/Sample · GitHub