Related
I'm trying to make an ImGui DX9 Window. However I get an error when I try to reference the wndproc function on the WNDCLASSEX structure.
When I try to pre-define the wndproc function as static I get an error: cannot overload static and non-static member functions with the same parameter types.
I already tried to call the function directly like so: menu::wnd_proc.
When I try to call the function without static I get this error on the function: a value of type "LRESULT (__stdcall menu::*)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)" cannot be used to initialize an entity of type "WNDPROC".
Here's my function
#include "ui.hpp"
bool menu::render( )
{
WNDCLASSEX wc = {
sizeof( WNDCLASSEX ),
CS_CLASSDC,
menu::wnd_proc,
0L,
0L,
GetModuleHandle( NULL ),
NULL,
NULL,
NULL,
NULL,
_T( "ImGui Example" ),
NULL
};
...
}
Here's the ui.hpp header file
#pragma once
class menu
{
public:
bool render( );
private:
LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
D3DPRESENT_PARAMETERS g_d3dpp = {};
static LRESULT WINAPI wnd_proc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
private:
...
LRESULT WINAPI wnd_proc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
if ( ImGui_ImplWin32_WndProcHandler( hWnd, msg, wParam, lParam ) )
return true;
switch ( msg )
{
case WM_SIZE:
if ( g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED )
{
g_d3dpp.BackBufferWidth = LOWORD( lParam );
g_d3dpp.BackBufferHeight = HIWORD( lParam );
menu::reset_device( );
}
return 0;
case WM_SYSCOMMAND:
if ( ( wParam & 0xfff0 ) == SC_KEYMENU )
return 0;
break;
case WM_DESTROY:
::PostQuitMessage( 0 );
return 0;
}
return ::DefWindowProc( hWnd, msg, wParam, lParam );
}
};
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
INTRODUCTION AND RELEVANT INFORMATION:
I have an edit control that should accept only signed decimal numbers-something like -123.456. Also, it should be locale aware, since decimal separator is not the same for every country-in US dot is used, while in Europe it is comma and so on.
MY EFFORTS TO SOLVE THIS:
So far I have used subclassing to implement this. Here is my logic for implementing the subclassing, expressed through pseudo code:
if ( ( character is not a [ digit,separator, or CTRL/Shift... ] OR
( char is separator and we already have one ) )
{
discard the character;
}
First I have made a helper function that determines if the char array already has a decimal separator, like this:
bool HasDecimalSeparator( wchar_t *test )
{
// get the decimal separator
wchar_t szBuffer[5];
GetLocaleInfo ( LOCALE_USER_DEFAULT,
LOCALE_SDECIMAL,
szBuffer,
sizeof(szBuffer) / sizeof(szBuffer[0] ) );
bool p = false; // text already has decimal separator?
size_t i = 0; // needed for while loop-iterator
// go through entire array and calculate the value of the p
while( !( p = ( test[i] == szBuffer[0] ) ) && ( i++ < wcslen(test) ) );
return p;
}
And here is the subclassing procedure-I haven't taken minus sign into account:
LRESULT CALLBACK Decimalni( HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass,
DWORD_PTR dwRefData )
{
switch (message)
{
case WM_CHAR:
{
// get decimal separator
wchar_t szBuffer[5];
GetLocaleInfo ( LOCALE_USER_DEFAULT,
LOCALE_SDECIMAL,
szBuffer,
sizeof(szBuffer) / sizeof(szBuffer[0] ) );
wchar_t t[50]; // here we store edit control's current text
memset( &t, L'\0', sizeof(t) );
// get edit control's current text
GetWindowText( hwnd, t, 50 );
// if ( ( is Not a ( digit,separator, or CTRL/Shift... )
// || ( char is separator and we already have one ) )
// discard the character
if( ( !( isdigit(wParam) || ( wParam == szBuffer[0] ) )
&& ( wParam >= L' ' ) ) // digit/separator/... ?
|| ( HasDecimalSeparator(t) // has separator?
&& ( wParam == szBuffer[0] ) ) )
{
return 0;
}
}
break;
}
return DefSubclassProc( hwnd, message, wParam, lParam);
}
One important note: I am able to load current user locale settings in my application, thanks to the answers to this question.
QUESTION:
Is there a better way to implement an edit control that accepts signed decimal numbers only, and is locale aware?
If subclassing is the only way, can my code be further improved/optimized ?
Thank you for your time and help.
Best regards.
APPENDIX:
To help you even further, here is a small demo application that creates an edit control and subclasses it to accept only decimal numbers-again, I haven't implemented the part for the minus sign:
#include <windows.h>
#include <commctrl.h>
#include <stdlib.h>
#include <locale.h>
#pragma comment( lib, "comctl32.lib")
const wchar_t g_szClassName[] = L"myWindowClass";
bool HasDecimalSeparator( wchar_t *test )
{
// get the decimal separator
wchar_t szBuffer[5];
GetLocaleInfo ( LOCALE_USER_DEFAULT,
LOCALE_SDECIMAL,
szBuffer,
sizeof(szBuffer) / sizeof(szBuffer[0] ) );
bool p = false; // text already has decimal separator?
size_t i = 0; // needed for while loop-iterator
// go through entire array and calculate the value of the p
while( !( p = ( test[i] == szBuffer[0] ) ) && ( i++ < wcslen(test) ) );
return p;
}
LRESULT CALLBACK Decimalni( HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass,
DWORD_PTR dwRefData )
{
switch (message)
{
case WM_CHAR:
{
// get decimal separator
wchar_t szBuffer[5];
GetLocaleInfo ( LOCALE_USER_DEFAULT,
LOCALE_SDECIMAL,
szBuffer,
sizeof(szBuffer) / sizeof(szBuffer[0] ) );
wchar_t t[50]; // here we store edit control's current text
memset( &t, L'\0', sizeof(t) );
// get edit control's current text
GetWindowText( hwnd, t, 50 );
// if ( ( is Not a ( digit,separator, or CTRL/Shift... )
// || ( char is separator and we already have one ) )
// discard the character
if( ( !( isdigit(wParam) || ( wParam == szBuffer[0] ) )
&& ( wParam >= L' ' ) ) // digit/separator/... ?
|| ( HasDecimalSeparator(t) // has separator?
&& ( wParam == szBuffer[0] ) ) )
{
return 0;
}
}
break;
}
return DefSubclassProc( hwnd, message, wParam, lParam);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
{
/************* load current locale settings *************/
// max. len: language, country, code page
wchar_t lpszLocale[64+64+16+3] = L"";
wchar_t lpszVal[128];
LCID nLCID = ::GetUserDefaultLCID(); // current LCID for user
if ( ::GetLocaleInfo( nLCID, LOCALE_SENGLANGUAGE, lpszVal, 128 ) )
{
wcscat_s( lpszLocale, 147, lpszVal ); // language
if ( ::GetLocaleInfo( nLCID, LOCALE_SENGCOUNTRY, lpszVal, 128 ) )
{
wcscat_s( lpszLocale, 147, L"_" ); // append country/region
wcscat_s( lpszLocale, 147, lpszVal );
if ( ::GetLocaleInfo( nLCID,
LOCALE_IDEFAULTANSICODEPAGE, lpszVal, 128 ) )
{
// missing code page or page number 0 is no error
// (e.g. with Unicode)
int nCPNum = _wtoi(lpszVal);
if (nCPNum >= 10)
{
wcscat_s( lpszLocale, 147, L"." ); // append code page
wcscat_s( lpszLocale, 147, lpszVal );
}
}
}
}
// set locale and LCID
_wsetlocale( LC_ALL, lpszLocale );
::SetThreadLocale(nLCID);
/*************************************************/
HWND hEdit1;
hEdit1 = CreateWindowEx(0, L"EDIT", L"",
WS_BORDER | WS_CHILD | WS_VISIBLE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
50, 100, 100, 20,
hwnd, (HMENU)8001, GetModuleHandle(NULL), NULL);
SetWindowSubclass( hEdit1, Decimalni, 0, 0);
}
break;
case WM_SETTINGCHANGE:
if( !wParam && !wcscmp( (wchar_t*)lParam, L"intl" ) )
{
// max. len: language, country, code page
wchar_t lpszLocale[64+64+16+3] = L"";
wchar_t lpszVal[128];
LCID nLCID = ::GetUserDefaultLCID(); // current LCID for user
if ( ::GetLocaleInfo( nLCID, LOCALE_SENGLANGUAGE, lpszVal, 128 ) )
{
wcscat_s( lpszLocale, 147, lpszVal ); // language
if ( ::GetLocaleInfo( nLCID, LOCALE_SENGCOUNTRY, lpszVal, 128 ) )
{
wcscat_s( lpszLocale, 147, L"_" ); // append country/region
wcscat_s( lpszLocale, 147, lpszVal );
if ( ::GetLocaleInfo( nLCID,
LOCALE_IDEFAULTANSICODEPAGE, lpszVal, 128 ) )
{
// missing code page or page number 0 is no error
// (e.g. with Unicode)
int nCPNum = _wtoi(lpszVal);
if (nCPNum >= 10)
{
wcscat_s( lpszLocale, 147, L"." ); // append code page
wcscat_s( lpszLocale, 147, lpszVal );
}
}
}
}
// set locale and LCID
_wsetlocale( LC_ALL, lpszLocale );
::SetThreadLocale(nLCID);
return 0L;
}
else
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, L"Window Registration Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
hwnd = CreateWindowEx(
0,
g_szClassName,
L"theForger's Tutorial Application",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 480, 320,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, L"Window Creation Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
Taking into consideration locale-specific settings
You certainly can do everything yourself, however you have an option to use VarI4FromStr or similar API which does dirty stuff for you. You put string in, you get LONG out. Locale aware.
"Should accept only"
You don't specify how the control should enforce this exactly. What if the input string is not valid? Control should be still accepting it because, for example, string is just not yet valid and user is still typing. If you are validating the input in external handler, such as when OK button is pressed, then you don't even need to subclass. If you want to check input every time it changes, you don't need to subclass either since you have EN_CHANGE notifications on parent. You might want to subclass for other reasons though.
It is user-friendly to accept any input and then indicate validity somehow (such as underlining with red if invalid) either on text change or on input validation.
After taking into account code from Advice required to insert one string into another once obtaining text from clipboard, I was able to put up a subclassing procedure that meets the requirement.
The point of my solution is to simulate edit control's behavior as mentioned in that post and then validate the resulting text.
When handling VK_DELETE, selected text is deleted and the result is then parsed to check if valid decimal format is left. If everything is OK message is passed to the default procedure else is discarded. The same method is performed for WM_CUT, WM_CLEAR, and for backspace in the WM_CHAR handler ( here we must protect ourself from crashing the app by accessing the element of the string with the ordinal -1, that is the reason why I have added the line if ( start > 0 ) ).
When handling WM_PASTE we merge the edit control's text with the clipboard text and then we parse the resulting string to check its validity. Again, if all is OK we pass the message else we discard it.
The same thing applies for WM_CHAR except we here insert character in the selected part of the edit control's text and then we perform the validity check.
Since the inputted text will be always correct this way, we do not have to handle WM_UNDO.
Finaly, here is the code:
LRESULT CALLBACK Decimalni( HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
switch (message)
{
case WM_KEYDOWN:
{
if( wParam == VK_DELETE )
{
DWORD start, end;
int len = GetWindowTextLength(hwnd);
std::wstring buffer( len, 0 );
// get current window text
if( len > 0 )
GetWindowText( hwnd, &buffer[0], len + 1 );
// get current selection
SendMessage( hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end );
if( end > start )
buffer.erase( start, end - start );
else
buffer.erase( start, 1 );
if( buffer.empty() )
return ::DefSubclassProc( hwnd, message, wParam, lParam);
bool IsTextValid = true; // indicates validity of inputed text
// TODO: parse buffer
if( IsTextValid )
return ::DefSubclassProc( hwnd, message, wParam, lParam);
else
{
// TODO: indicate error
return FALSE;
}
}
}
return ::DefSubclassProc( hwnd, message, wParam, lParam);;
break;
case WM_CLEAR:
case WM_CUT:
{
DWORD start, end;
int len = GetWindowTextLength(hwnd);
std::wstring buffer( len, 0 );
// get current window text
if( len > 0 )
GetWindowText( hwnd, &buffer[0], len + 1 );
// get current selection
SendMessage( hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end );
if( end > start )
buffer.erase( start, end - start );
if( buffer.empty() )
return ::DefSubclassProc( hwnd, message, wParam, lParam);
// TODO: parse buffer
bool IsTextValid = true;
if( IsTextValid )
return ::DefSubclassProc( hwnd, message, wParam, lParam);
else
{
// TODO: Indicate error
return FALSE;
}
}
break;
case WM_PASTE:
{
int len = GetWindowTextLength(hwnd);
std::wstring clipboard, wndtxt( len, 0 );
if( len > 0 )
GetWindowText( hwnd, &wndtxt[0], len + 1 );
if( !OpenClipboard(hwnd) )
return FALSE;
HANDLE hClipboardData;
if( hClipboardData = GetClipboardData(CF_UNICODETEXT) )
{
clipboard = (wchar_t*)GlobalLock(hClipboardData);
GlobalUnlock(hClipboardData);
}
CloseClipboard();
if( clipboard.empty() )
return FALSE;
DWORD start, end;
SendMessage( hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end );
// merge strings into one
if( end > start )
wndtxt.replace( start, end - start, clipboard );
else
wndtxt.insert( start, clipboard );
// TODO: parse the text
bool ITextValid = true;
// process the result
if( IsTextValid )
return ::DefSubclassProc( hwnd, message, wParam, lParam);
else
{
// TODO: indicate error
return FALSE;
}
}
break;
case WM_CHAR:
{
DWORD start, end;
int len = GetWindowTextLength(hwnd);
std::wstring buffer( len, 0 );
// get current window text
if( len > 0 )
GetWindowText( hwnd, &buffer[0], len + 1 );
// get current selection
SendMessage( hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end );
// allow copy/paste but leave backspace for special handler
if( ( wParam < 0x020 ) && ( wParam != 0x08 ) )
return ::DefSubclassProc( hwnd, message, wParam, lParam);}
// process backspace
if( wParam == 0x08 )
{
if( end > start )
buffer.erase( start, end - start );
else
if( start > 0 ) // it is safe to move back one place
buffer.erase( start - 1, 1 );
else // start-1 < 0 , can't access buffer[-1] !!
return FALSE;
if( buffer.empty() )
return ::DefSubclassProc( hwnd, message, wParam, lParam);
// TODO: parse buffer
// process the result
if( IsTextValid )
return ::DefSubclassProc( hwnd, message, wParam, lParam);
else
{
//TODO: indicate error
return FALSE;
}
}
// insert character and parse text
if( end > start )
buffer.replace( start, end - start, 1, (wchar_t)wParam );
else
buffer.insert( start, 1, (wchar_t)wParam );
// TODO: parse text
// process the result
if( IsTextValid )
return ::DefSubclassProc( hwnd, message, wParam, lParam);
else
{
//TODO: indicate error
return FALSE;
}
}
break;
case WM_NCDESTROY:
::RemoveWindowSubclass( hwnd, Decimalni, 0 );
break;
}
return ::DefSubclassProc( hwnd, message, wParam, lParam);
}
I'm trying to hook a button click in a mfc window.And I used WM_COMMAND and WM_NOTIFY.For test, if the button is clicked, the hook must create a messagebox. The problem is that it doesn't react to button clicks.
Here's the code for reacting to WM_COMMAND and WM_NOTIFY:
LPMSG msg = (LPMSG)lParam;
switch( msg->message )
{
case WM_COMMAND:
MessageBox( NULL,"HOOK","YOOOO",MB_ICONEXCLAMATION );
break;
case WM_NOTIFY:
MessageBox( NULL,"HOOK","YOOOOO",MB_ICONEXCLAMATION );
break;
}
And here's the code to hole dll:
#include <Windows.h>
#include "FindingWindow.h"
#pragma comment( linker,"/SECTION:.SHARED,RWS" )
#pragma data_seg( ".SHARED" )
CaptureTextWindow* ctw;
HHOOK hook = 0;
HMODULE hInstance = 0;
HWND hWndServer = NULL;
#pragma data_seg()
static LRESULT CALLBACK msghook(int nCode, WPARAM wParam, LPARAM lParam);
__declspec(dllexport) BOOL clearMyHook(HWND hWnd);
BOOL APIENTRY DllMain( HINSTANCE hInst, DWORD ul_reason_for_call, LPVOID lpReserved )
{
switch( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH:
hInstance = hInst;
return TRUE;
case DLL_PROCESS_DETACH:
if(hWndServer != NULL)
clearMyHook(hWndServer);
return TRUE;
}
return TRUE;
}
__declspec(dllexport) BOOL WINAPI setMyHook(HWND hWnd)
{
if(hWndServer != NULL)
return FALSE;
hook = SetWindowsHookEx(
WH_CALLWNDPROC,
(HOOKPROC)msghook,
hInstance,
0);
if(hook != NULL)
{ /* success */
hWndServer = hWnd;
return TRUE;
} /* success */
return FALSE;
}
__declspec(dllexport) BOOL clearMyHook(HWND hWnd)
{
if(hWnd != hWndServer)
return FALSE;
BOOL unhooked = UnhookWindowsHookEx(hook);
if(unhooked)
hWndServer = NULL;
return unhooked;
}
static LRESULT CALLBACK msghook( int nCode, // hook code
WPARAM wParam , // message identifier
LPARAM lParam )
{
if( nCode < 0 )
{
CallNextHookEx( hook, nCode, wParam, lParam );
return 0;
}
LPMSG msg = (LPMSG)lParam;
switch( msg->message )
{
case WM_COMMAND:
MessageBox( NULL,"HOOK","YOOOO",MB_ICONEXCLAMATION );
break;
case WM_NOTIFY:
MessageBox( NULL,"HOOK","YOOOOO",MB_ICONEXCLAMATION );
break;
}
return CallNextHookEx( hook, nCode, wParam, lParam );
}
I used not only WM_COMMAND cause I thought mb it will work, but it didn't.Thanks for answer.
The lParam for a WH_CALLWNDPROC hook is of type CWPSTRUCT. Your CallWndProc should look like this:
// ...
const CWPSTRUCT& cwps = *(CWPSTRUCT*)lParam;
switch ( cwps.message ) {
case WM_COMMAND:
MessageBox( ... );
break;
// ...
I've been trying to figure this out for ages and I'm absolutely stumped! I'm trying to subclass an edit control so I can capture when the enter key is pressed.
I've seen lots of other posts about sub-classing with snippets of code to add to do it, but I can't seem to implement it into my application. I apologize if I'm making a stupid mistake, but I simply cannot figure this out.
I know this code is poorly written and has no error checking, but I wanted to post as little code as possible to convey the problem.
#include <Windows.h>
#include <wchar.h>
HWND editHWND;
WNDPROC wpOrigEditProc;
LRESULT APIENTRY EditSubclassProc(HWND hwnd,UINT uMsg,WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_CHAR)
{
//do my stuff
}
return CallWindowProc(wpOrigEditProc, hwnd, uMsg, wParam, lParam);
}
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_CREATE:
wpOrigEditProc = (WNDPROC) SetWindowLong(editHWND,GWL_WNDPROC, (LONG) EditSubclassProc);
SetWindowLong(editHWND, GWL_WNDPROC,(LONG) wpOrigEditProc);
break;
case WM_DESTROY:
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( hWnd, msg, wParam, lParam );
}
int WINAPI wWinMain( HINSTANCE hInst,HINSTANCE,LPWSTR,INT )
{
WNDCLASSEX wc = { sizeof( WNDCLASSEX ),CS_CLASSDC,MsgProc,0,0,
GetModuleHandle( NULL ),NULL,NULL,NULL,NULL,
L"My Window",NULL };
RegisterClassEx( &wc );
HWND hWnd = CreateWindowW( L"My Window",L"test application",
WS_OVERLAPPEDWINDOW,100,100,800,600,
NULL,NULL,wc.hInstance,NULL );
editHWND = CreateWindow( L"edit",L"hi there",WS_VISIBLE | WS_CHILD | WS_BORDER,100,100,300,50,hWnd,(HMENU)17,0,0);
ShowWindow( hWnd,SW_SHOWDEFAULT );
MSG msg;
ZeroMemory( &msg,sizeof( msg ) );
while( msg.message != WM_QUIT )
{
if( PeekMessage( &msg,NULL,0,0,PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
UnregisterClass( L"My Window",wc.hInstance );
return 0;
}
MsgProc() is the window procedure for the "My Window" window. When you are processing the WM_CREATE message, you are processing that window's creation, before the second CreateWindow() has been called to create the Edit window.
Worse, even if you were subclassing the Edit window correctly, you are removing the subclass immediately after setting it. So EditSubClassProc() would never have a change to be called anyway.
Since you are not defining a custom window procedure for the Edit window at the time it is being created, you cannot use a WM_CREATE message to subclass the Edit window (unless you use a message hook, which is overkill in this situation). Just call SetWindowLong() after CreateWindow() has exited.
Try this:
#include <Windows.h>
#include <wchar.h>
HWND editHWND = NULL;
WNDPROC wpOrigEditProc = NULL;
LRESULT APIENTRY EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_CHAR)
{
//do my stuff
}
return CallWindowProc(wpOrigEditProc, hwnd, uMsg, wParam, lParam);
}
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_CLOSE:
DestroyWindow(hWnd);
return 0;
case WM_DESTROY:
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( hWnd, msg, wParam, lParam );
}
int WINAPI wWinMain( HINSTANCE hInst,HINSTANCE,LPWSTR,INT )
{
WNDCLASSEX wc = { sizeof( WNDCLASSEX ),CS_CLASSDC,MsgProc,0,0,
GetModuleHandle( NULL ),NULL,NULL,NULL,NULL,
TEXT("My Window"),NULL };
RegisterClassEx( &wc );
HWND hWnd = CreateWindowW( TEXT("My Window"),TEXT("test application"),
WS_OVERLAPPEDWINDOW,100,100,800,600,
NULL,NULL,wc.hInstance,NULL );
editHWND = CreateWindow( TEXT("edit"),TEXT("hi there"),WS_VISIBLE | WS_CHILD | WS_BORDER,100,100,300,50,hWnd,(HMENU)17,0,0);
wpOrigEditProc = (WNDPROC) SetWindowLongPtr(editHWND, GWL_WNDPROC, (LONG_PTR) EditSubclassProc);
ShowWindow( hWnd, SW_SHOWDEFAULT );
MSG msg;
while ( GetMessage( &msg,NULL,0,0 ) > 0 )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
UnregisterClass( TEXT("My Window"),wc.hInstance );
return 0;
}
With that said, another way to subclass a window is to use SetWindowSubclass() instead:
#include <Windows.h>
#include <wchar.h>
HWND editHWND = NULL;
LRESULT CALLBACK EditSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
if (uMsg == WM_CHAR)
{
//do my stuff
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_CLOSE:
DestroyWindow(hWnd);
return 0;
case WM_DESTROY:
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( hWnd, msg, wParam, lParam );
}
int WINAPI wWinMain( HINSTANCE hInst,HINSTANCE,LPWSTR,INT )
{
WNDCLASSEX wc = { sizeof( WNDCLASSEX ),CS_CLASSDC,MsgProc,0,0,
GetModuleHandle( NULL ),NULL,NULL,NULL,NULL,
TEXT("My Window"),NULL };
RegisterClassEx( &wc );
HWND hWnd = CreateWindowW( TEXT("My Window"),TEXT("test application"),
WS_OVERLAPPEDWINDOW,100,100,800,600,
NULL,NULL,wc.hInstance,NULL );
editHWND = CreateWindow( TEXT("edit"),TEXT("hi there"),WS_VISIBLE | WS_CHILD | WS_BORDER,100,100,300,50,hWnd,(HMENU)17,0,0);
SetWindowSubclass(editWND, EditSubclassProc, 0, 0);
ShowWindow( hWnd, SW_SHOWDEFAULT );
MSG msg;
while ( GetMessage( &msg,NULL,0,0 ) > 0 )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
UnregisterClass( TEXT("My Window"),wc.hInstance );
return 0;
}
I have three classes...Base, Derived 1, and Derived 2. The base class holds a static LONG( this * ) that it uses in a static function to handle window messages. The problem that I'm having is that when I declare multiple derived classes, the static LONG within the base class is changed upon the second derived class declaration...here is implementation:
BaseDialog.h:
class CBaseDialog;
typedef void(CBaseDialog::*fpMessageHandler)(HWND hDlg,WPARAM wParam,LPARAM lParam);
struct t_MessageEntry
{
fpMessageHandler MsgHandler;
};
/////////////////////////////// MACROS
#define IMPLEMENT_MESSAGE_HANDLER(base,derived) void derived::AddHandler(UINT MessageId, void(derived::*Handler)(HWND hDlg,WPARAM wParam,LPARAM lParam))\
{\
AddMessageHandler(MessageId, (void(base::*)(HWND hDlg,WPARAM wParam,LPARAM lParam))Handler);\
}\
#define DECLARE_MESSAGE_HANDLER(derived) void AddHandler(UINT MessageId, void(derived::*Handler)(HWND hDlg,WPARAM wParam,LPARAM lParam));\
void HandleManager(void);\
#define BEGIN_MESSAGE_MAP(derived) void derived::HandleManager(void) {
#define ADD_MESSAGE_HANDLER(message,handler) AddHandler(message, handler);
#define END_MESSAGE_MAP() }
#define ENABLE_MESSAGE_MAP() HandleManager();
class CBaseDialog
{
public:
std::map<UINT,t_MessageEntry> m_MessageMap;
std::map<UINT,t_MessageEntry>::iterator m_MessageMapIterator;
CBaseDialog(int nResId, HWND hParent=NULL);
virtual ~CBaseDialog();
int DoModal(void);
static BOOL CALLBACK DialogProcStatic(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
void OnOK(void);
void OnCancel(void);
void AddMessageHandler(UINT MessageId, void(CBaseDialog::*Handler)(HWND hDlg,WPARAM wParam,LPARAM lParam));
protected:
int m_nResId;
HWND m_hParent;
static HWND m_hWindow;
static long m_lSaveThis;
};
BaseDialog.cpp:
HWND CBaseDialog::m_hWindow = NULL;
long CBaseDialog::m_lSaveThis = 0; // Changes on second declaration of derived class
CBaseDialog::CBaseDialog(int nResId, HWND hParent)
{
m_lSaveThis = (long)this; /// store this pointer
m_nResId = nResId;
m_hParent = hParent;
}
CBaseDialog::~CBaseDialog()
{
m_hWindow = NULL;
m_lSaveThis = 0;
}
int CBaseDialog::DoModal(void)
{
HWND hWnd = CreateDialog( GetModuleHandle( NULL ), MAKEINTRESOURCE( m_nResId ), m_hParent, ( DLGPROC )DialogProcStatic );
return 0;
}
void CBaseDialog::AddMessageHandler(UINT MessageId, void(CBaseDialog::*MsgHandler)(HWND hDlg,WPARAM wParam,LPARAM lParam))
{
t_MessageEntry MessageEntry;
MessageEntry.MsgHandler = MsgHandler;
m_MessageMap.insert(std::map<UINT,t_MessageEntry>::value_type(MessageId, MessageEntry)); /// insert key & data to map
}
BOOL CALLBACK CBaseDialog::DialogProcStatic(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if(m_hWindow == NULL)
{
m_hWindow = hDlg;
}
CBaseDialog *pThis = (CBaseDialog*)m_lSaveThis; /// typecast stored this-pointer to CBaseDialog pointer
return pThis->DialogProc( hDlg, uMsg, wParam, lParam );
}
BOOL CALLBACK CBaseDialog::DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
m_MessageMapIterator = m_MessageMap.find(message); /// find message entry by key
if(m_MessageMapIterator == m_MessageMap.end()) /// check if message entry available
{
return 0;
}
else
{
t_MessageEntry MessageEntry = (*m_MessageMapIterator).second; /// dereference iterator and get message entry
void (CBaseDialog::*MessageHandler)(HWND hDlg,WPARAM wParam,LPARAM lParam);
MessageHandler = MessageEntry.MsgHandler;
(this->*MessageHandler)(hDlg, wParam, lParam); /// execute function
return 0;
}
}
void CBaseDialog::OnOK(void)
{
EndDialog(m_hWindow, IDOK);
}
void CBaseDialog::OnCancel(void)
{
EndDialog(m_hWindow, IDCANCEL);
}
Outliner.h:
#include "BaseDialog.h"
class COutlinerDlg : public CBaseDialog
{
public:
COutlinerDlg( int nResId, HWND hParent=NULL );
virtual ~COutlinerDlg();
void Initialize( LPCWSTR strRootName )
{
m_strRootName = strRootName;
}
public:
VOID Resize( RECT rc );
HWND GetHWND(){ return m_hWindow; }
HWND GetTREEDLG(){ return m_hTreeDlg; }
BOOL GetVisible(){ return m_bVisible; }
VOID SetVisible( BOOL b ){ m_bVisible = b; }
BOOL GetDragging(){ return m_bDragging; }
VOID SetDragging( BOOL b ){ m_bDragging = b; }
VOID SetParentHWND( HWND hWnd ){ m_hParent = hWnd; }
HWND GetParentHWND(){ return m_hParent; }
BOOL Show( DWORD dwFlags ){ return ShowWindow( m_hWindow, dwFlags ); }
HRESULT BuildOutlinerFromDirectory( LPCWSTR rootName, LPCWSTR directory );
HRESULT BuildChildDirectory( LPCWSTR child );
protected:
void On_WM_INITDIALOG( HWND hDlg, WPARAM wParam, LPARAM lParam );
void On_WM_COMMAND( HWND hDlg, WPARAM wParam, LPARAM lParam );
void On_WM_NOTIFY( HWND hDlg, WPARAM wParam, LPARAM lParam );
void On_WM_LBUTTONDOWN( HWND hDlg, WPARAM wParam, LPARAM lParam );
void On_WM_LBUTTONUP( HWND hDlg, WPARAM wParam, LPARAM lParam );
void On_WM_MOUSEMOVE( HWND hDlg, WPARAM wParam, LPARAM lParam );
void On_WM_PAINT( HWND hDlg, WPARAM wParam, LPARAM lParam );
void On_WM_SIZE( HWND hDlg, WPARAM wParam, LPARAM lParam );
void On_WM_CLOSE( HWND hDlg, WPARAM wParam, LPARAM lParam );
DECLARE_MESSAGE_HANDLER(COutlinerDlg);
private:
// Tree Root name
LPCWSTR m_strRootName;
// Directory
LPCWSTR m_strDirectory;
// Dialog Dimensions
RECT m_rcDlg;
TV_ITEM m_tvi;
TV_INSERTSTRUCT m_tvinsert; // struct to config out tree control
HTREEITEM m_hTISelected;
HTREEITEM m_hTIParent; // Tree item handle
HTREEITEM m_hTIBefore; // .......
HTREEITEM m_hTIRoot; // .......
HIMAGELIST m_hImageList; // Image list array hadle
bool m_bSelected;
// for drag and drop
HWND m_hTreeDlg;
HTREEITEM m_hTIHitTarget;
TVHITTESTINFO m_tvht;
POINTS m_ptsPos;
bool m_bDragging;
bool m_bVisible;
// for lable editing
HWND m_hEdit;
};
Outliner.cpp:
#include "Outliner.h"
IMPLEMENT_MESSAGE_HANDLER( CBaseDialog, COutlinerDlg )
BEGIN_MESSAGE_MAP( COutlinerDlg )
ADD_MESSAGE_HANDLER( WM_INITDIALOG, &COutlinerDlg::On_WM_INITDIALOG )
ADD_MESSAGE_HANDLER( WM_COMMAND, &COutlinerDlg::On_WM_COMMAND )
ADD_MESSAGE_HANDLER( WM_NOTIFY, &COutlinerDlg::On_WM_NOTIFY )
ADD_MESSAGE_HANDLER( WM_LBUTTONDOWN, &COutlinerDlg::On_WM_LBUTTONDOWN )
ADD_MESSAGE_HANDLER( WM_LBUTTONUP, &COutlinerDlg::On_WM_LBUTTONUP )
ADD_MESSAGE_HANDLER( WM_MOUSEMOVE, &COutlinerDlg::On_WM_MOUSEMOVE )
ADD_MESSAGE_HANDLER( WM_PAINT, &COutlinerDlg::On_WM_PAINT )
ADD_MESSAGE_HANDLER( WM_CLOSE, &COutlinerDlg::On_WM_CLOSE )
END_MESSAGE_MAP( )
COutlinerDlg::COutlinerDlg( int nResId, HWND hParent ) : CBaseDialog( nResId, hParent )
{
ENABLE_MESSAGE_MAP( );
m_hTISelected = m_hTIParent = m_hTIBefore = m_hTIRoot = m_hTIHitTarget = NULL;
m_hImageList = NULL;
m_bSelected = m_bDragging = false;
m_bVisible = true;
m_hTreeDlg = NULL;
ZeroMemory( &m_tvi, sizeof( TV_ITEM ) );
ZeroMemory( &m_tvinsert, sizeof( TV_INSERTSTRUCT ) );
ZeroMemory( &m_tvht, sizeof( TVHITTESTINFO ) );
ZeroMemory( &m_ptsPos, sizeof( POINTS ) );
}
COutlinerDlg::~COutlinerDlg( )
{
m_hWindow = NULL;
m_lSaveThis = 0;
}
void COutlinerDlg::On_WM_INITDIALOG( HWND hDlg, WPARAM wParam, LPARAM lParam )
{
...
}
This code is from a demo I found online at code project I think...
Can I instance the base class so that I do not overwrite the static LONG when declaring a new instance of Outliner?
Even if you instantiate the base class, the static long m_lSaveThis; will still change its value when you instantiate a new Outliner.
The reason:
Since, m_lSaveThis is static, it has only one copy in memory, and this code in your Base class constructor m_lSaveThis = (long)this; will be invoked for every instance of CBaseDialog or COutlinerDlg because COutlinerDlg inherits from CBaseDialog so it also invokes its constructor. With this code, m_lSaveThis will only point to the latest instance that you created whether the base class or the derived class
Here you are using static long m_lSaveThis so static declares are common for all instance of the class (it is class level not instance level). So why you use static? I think your requirement can reach if you declare m_lSaveThis without static.
protected:
long m_lSaveThis;
CBaseDialog::CBaseDialog(int nResId, HWND hParent)
{
m_lSaveThis = (long)this;
}
There is my code, declare a static map object to handle the class instance and window handle. The bellow header file show the sample code:
CDialogBase
{
...
static BOOL CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
static std::map<HWND, CDialogBase*> m_mapInstance;
}
And implement the DialogProc like this:
BOOL CALLBACK CDialogBase::DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
CDialogBase * pThis = NULL;
std::map<HWND, CDialogBase*>::iterator it = m_mapInstance.find(hwnd);
if (it != m_mapInstance.end())
{
pThis = m_mapInstance[hwnd];
}
switch (msg)
{
case WM_INITDIALOG:
{
if (pThis != NULL)
{
m_mapInstance.erase(hwnd);
pThis = NULL;
}
if (lParam == NULL)
{
return FALSE; //Should start dialog by DialogBoxParam and lParam must not be null.
}
pThis = (CDialogBase*)lParam;
m_mapInstance.insert(std::map<HWND, CDialogBase*>::value_type(hwnd, pThis));
pThis->OnInitDialog();
}
break;
case WM_DESTROY:
{
if (pThis != NULL)
{
pThis->OnDestroy();
m_mapInstance.erase(hwnd);
}
}
default:
break;
}
if(pThis != NULL)
return pThis->OnDefaultDialogProc(msg, wParam, lParam); //Must implement this function and default return FALSE.
else
return FALSE;
}
And, show the dialog:
UINT CDialogBase::DoModal(HINSTANCE hInst, HWND hParent, UINT nDlgID)
{
m_nDialogResourceID = nDlgID;
return ::DialogBoxParam(hInst, MAKEINTRESOURCE(nDlgID),
hParent, DialogProc, (LPARAM) this);
}
I hope this is helpful for you.