I have multiple edit control
s in a dialog box
that need to accept floating numbers and be locale
aware. I have decided to implement this behavior with subclassing
the edit control
s.
To achieve locale
awareness I have decided to pass a structure to the SetWindowSubclass
( it will hold data about decimal separator, negative sign and so on... ) as a dwRefData
parameter.
The value of the fields is set in WM_CREATE
but they may change if user changes settings in the Control Panel->Regional and Language Settings
so I have decided to update the field's values in response to the WM_SETTINGCHANGE
message.
To avoid making this structure global, I have decided to make it static
and declare it in the main window's procedure. I shall pass it to the dialog as the LPARAM
argument of the CreateDialogParam
API.
I do not know how to properly pass the above described structure to CreateDialogParam
and SetWindowSubclass
.
The problem is also the proper removal of the subclass
so memory leaks can be avoided.
After searching through Internet I failed to find anything useful, so for now I just have a conceptual solution.
It should look something like this:
//declare our structure that holds locale info
struct locale_structure
{
wchar_t NegativeSign;
wchar_t separator;
};
//in my main window procedure:
static locale_structure MyStruct;
case WM_CREATE:
{
//get locale info about separator, negative sign and so on...
MyStruct->separator = //...
MyStruct->NegativeSign = //...
}
return 0L;
case WM_SETTINGCHANGE:
{
//update locale info about separator, negative sign and so on...
MyStruct->NegativeSign = //...
MyStruct->separator = //...
//...
}
// in the WM_COMMAND, to the response to a button click, pass MyStruct:
hwndSomeDlg = CreateDialogParam( ..., (LPARAM)MyStruct );
In my dialog box
I should subclass
/ remove subclass
like this:
case WM_INITDIALOG:
{
/******** This is the tricky part ***********/
/// get locale structure from lParam
locale_structure MyStruct = ( locale_structure )lParam;
// pass it properly to subclassing procedures
SetWindowSubclass( GetDlgItem( hDlg, IDC_EDIT1 ), ..., MyStruct );
SetWindowSubclass( GetDlgItem( hDlg, IDC_EDIT2 ), ..., MyStruct );
/************** other stuff ***********************/
}
return TRUE;
case WM_CLOSE:
{
// perform proper cleanup to avoid memory leaks
RemoveWindowSubclass( ... );
}
How should I pass MyStruct
to the CreateDialogParam
and SetWindowSubclass
?
How should I remove subclassing
so I can avoid memory leaks?
I assume that some memory allocating and deallocating will occur in subclassing procedure
as well, can you instruct me how to do this properly?
I am submitting the code that implements the concept described in the section MY EFFORTS TO SOLVE THIS.
The program makes main window with a button that launches a dialog box. The dialog box contains two subclassed edit controls. Everything works fine, so my only concern now is if I am deleting structures properly.
Just make an empty project and copy/paste the required files:
In resource.h
:
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by resource.rc
//
#define IDD_DIALOG1 101
#define IDC_EDIT1 1001
#define IDC_EDIT2 1002
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1003
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
In resource.rc
:
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_DIALOG1 DIALOGEX 0, 0, 316, 180
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,205,159,50,14
PUSHBUTTON "Cancel",IDCANCEL,259,159,50,14
EDITTEXT IDC_EDIT1,23,20,94,17,ES_AUTOHSCROLL
EDITTEXT IDC_EDIT2,23,65,90,21,ES_AUTOHSCROLL
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_DIALOG1, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 309
TOPMARGIN, 7
BOTTOMMARGIN, 173
END
END
#endif // APSTUDIO_INVOKED
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
In main.cpp
:
#include <windows.h>
#include <commctrl.h>
#include <stdlib.h>
#include <locale.h>
#include <stdio.h>
#pragma comment( lib, "comctl32.lib")
const wchar_t g_szClassName[] = L"myWindowClass";
// structure to hold locale info
typedef struct LocaleInfo
{
wchar_t NegativeSign[5];
wchar_t DecimalSeparator[5];
}*pLocaleInfo;
// subclassing procedure
LRESULT CALLBACK Decimalni( HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
pLocaleInfo li = ( pLocaleInfo )dwRefData;
switch (message)
{
case WM_CHAR:
{
// accept digits,decimal separatorand negative sign
// this validation is just an example
if( ( isdigit(wParam) ) || ( wParam < L' ' ) )
break;
else
{
for( size_t i = 0; i < 5; i++ )
if( ( wParam == li->DecimalSeparator[i] )
|| ( wParam == li->NegativeSign[i] ) )
{
return DefSubclassProc( hwnd, message, wParam, lParam);
break;
}
else
{
MessageBeep(0);
return FALSE;
break;
}
}
}
break;
case WM_NCDESTROY:
// should I delete structure here ??
RemoveWindowSubclass( hwnd, Decimalni, 0 );
break;
}
return DefSubclassProc( hwnd, message, wParam, lParam);
}
// dialog box procedure
INT_PTR CALLBACK DlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
case WM_INITDIALOG:
{
// subclass edit control
pLocaleInfo li = ( pLocaleInfo )lParam;
SetWindowSubclass( GetDlgItem( hwnd, IDC_EDIT1 ),
Decimalni, 0, (DWORD_PTR)li );
SetWindowSubclass( GetDlgItem( hwnd, IDC_EDIT2 ),
Decimalni, 0, (DWORD_PTR)li );
}
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
case IDCANCEL:
EndDialog(hwnd, IDCANCEL);
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static pLocaleInfo li; // store decimal separator and minus sign
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);
/*** get information for decimal separator and negative sign ***/
li = new LocaleInfo;
GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
li->DecimalSeparator,
sizeof(li->DecimalSeparator) / sizeof(li->DecimalSeparator[0] ) );
GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_SNEGATIVESIGN,
li->NegativeSign,
sizeof(li->NegativeSign) / sizeof(li->NegativeSign[0] ) );
/*** create a button that launches a test dialog box *****/
HWND hButton = CreateWindowEx(0, L"Button",
L"Test subclassed edit controls in dialog box",
WS_BORDER | WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
50, 50, 350, 80, hwnd, (HMENU)8001,
GetModuleHandle(NULL), NULL);
}
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);
/** update information for decimal separator and negative sign ***/
GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
li->DecimalSeparator,
sizeof(li->DecimalSeparator) / sizeof(li->DecimalSeparator[0] ) );
GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_SNEGATIVESIGN,
li->NegativeSign,
sizeof(li->NegativeSign) / sizeof(li->NegativeSign[0] ) );
return 0L;
}
else
break;
case WM_COMMAND:
{
if( LOWORD(wParam) == 8001 )
{
DialogBoxParam( GetModuleHandle(NULL),
MAKEINTRESOURCE(IDD_DIALOG1), hwnd, DlgProc, (LPARAM)li );
}
}
return 0L;
case WM_CLOSE:
delete li;
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;
}
Thank you for your time and help.
Best regards.
EDIT 2 :
Here's a working example (I hope):
#include "resource.h"
#include <windows.h>
#include <commctrl.h>
#include <stdlib.h>
#include <locale.h>
#include <stdio.h>
#pragma comment( lib, "comctl32.lib")
const wchar_t g_szClassName[] = L"myWindowClass";
// structure to hold locale info
typedef struct LocaleInfo
{
size_t NegativeSize, DecimalSize;
wchar_t *NegativeSign, *DecimalSeparator;
LocaleInfo()
{
NegativeSize = 0;
DecimalSize = 0;
NegativeSign = NULL;
DecimalSeparator = NULL;
}
~LocaleInfo()
{
if (NegativeSign != NULL)
{
delete[] NegativeSign;
NegativeSign = NULL;
}
if (DecimalSeparator != NULL)
{
delete[] DecimalSeparator;
DecimalSeparator = NULL;
}
}
}*pLocaleInfo;
void ReallocateInfo(wchar_t *&InsideArray, size_t &InsideSize, size_t NewSize)
{
if (NewSize == 0)
return;
wchar_t *NewArray = new wchar_t[NewSize];
delete[] InsideArray;
InsideArray = NewArray;
InsideSize = NewSize;
}
// subclassing procedure
LRESULT CALLBACK Decimalni(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
pLocaleInfo li = (pLocaleInfo)dwRefData;
switch (message)
{
case WM_CHAR:
{
// accept digits,decimal separatorand negative sign
// this validation is just an example
if ((isdigit(wParam)) || (wParam < L' '))
break;
else
{
for (size_t i = 0; i < 5; i++)
if ((wParam == li->DecimalSeparator[i])
|| (wParam == li->NegativeSign[i]))
{
return DefSubclassProc(hwnd, message, wParam, lParam);
break;
}
else
{
MessageBeep(0);
return FALSE;
break;
}
}
}
break;
case WM_NCDESTROY:
// should I delete structure here ??
RemoveWindowSubclass(hwnd, Decimalni, 0);
break;
}
return DefSubclassProc(hwnd, message, wParam, lParam);
}
// dialog box procedure
INT_PTR CALLBACK DlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch (Message)
{
case WM_INITDIALOG:
{
// subclass edit control
pLocaleInfo li = (pLocaleInfo)lParam;
SetWindowSubclass(GetDlgItem(hwnd, IDC_EDIT1),
Decimalni, 0, (DWORD_PTR)li);
SetWindowSubclass(GetDlgItem(hwnd, IDC_EDIT2),
Decimalni, 0, (DWORD_PTR)li);
}
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
case IDCANCEL:
EndDialog(hwnd, IDCANCEL);
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static pLocaleInfo li; // store decimal separator and minus sign
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);
/*** get information for decimal separator and negative sign ***/
li = new LocaleInfo;
ReallocateInfo(li->DecimalSeparator, li->DecimalSize, GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, NULL, NULL));
ReallocateInfo(li->NegativeSign, li->NegativeSize, GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SNEGATIVESIGN, NULL, NULL));
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
li->DecimalSeparator,
li->DecimalSize);
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SNEGATIVESIGN,
li->NegativeSign,
li->NegativeSize);
/*** create a button that launches a test dialog box *****/
HWND hButton = CreateWindowEx(0, L"Button",
L"Test subclassed edit controls in dialog box",
WS_BORDER | WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
50, 50, 350, 80, hwnd, (HMENU)8001,
GetModuleHandle(NULL), NULL);
}
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);
/** update information for decimal separator and negative sign ***/
ReallocateInfo(li->DecimalSeparator, li->DecimalSize, GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, NULL, NULL));
ReallocateInfo(li->NegativeSign, li->NegativeSize, GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SNEGATIVESIGN, NULL, NULL));
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
li->DecimalSeparator,
li->DecimalSize);
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SNEGATIVESIGN,
li->NegativeSign,
li->NegativeSize);
return 0L;
}
else
break;
case WM_COMMAND:
{
if (LOWORD(wParam) == 8001)
{
DialogBoxParam(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDD_DIALOG1), hwnd, DlgProc, (LPARAM)li);
}
}
return 0L;
case WM_CLOSE:
{
delete li;
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;
}
EDIT:
Everything is fine. You are right to delete it on WM_NCDESTROY
, because it wil always be called (WM_CLOSE
sometimes might be skipped). Still you forgot {}
on WM_CLOSE
, so there might be some problems when compiler decides you need problem.
Smart pointers should be used, because it should free memory for you in case something goes wrong, but it's up to you to choose. Also one good thing about them is that it is very easy to adapt to your code, you basically change nothing. One last thing, this "SmartPointer" is my creation and might still be incomplete, the other alternatives are unique_ptr
, shared_ptr
and many more which can be found on internet.
There you go:
#include "resource.h"
#include <Windows.h>
#include <commctrl.h>
#include <stdlib.h>
#include <locale.h>
#include <stdio.h>
#include "SmartPointer.h"
#pragma comment( lib, "comctl32.lib")
const wchar_t g_szClassName[] = L"myWindowClass";
// structure to hold locale info
struct LocaleInfo
{
wchar_t NegativeSign[5];
wchar_t DecimalSeparator[5];
};
// subclassing procedure
LRESULT CALLBACK Decimalni(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
SmartPointer<LocaleInfo> *li = (SmartPointer<LocaleInfo>*)dwRefData;
switch (message)
{
case WM_CHAR:
{
// accept digits,decimal separatorand negative sign
// this validation is just an example
if ((isdigit(wParam)) || (wParam < L' '))
break;
else
{
for (size_t i = 0; i < 5; i++)
if ((wParam == (*li)->DecimalSeparator[i])
|| (wParam == (*li)->NegativeSign[i]))
{
return DefSubclassProc(hwnd, message, wParam, lParam);
break;
}
else
{
MessageBeep(0);
return FALSE;
break;
}
}
}
break;
case WM_NCDESTROY:
// should I delete structure here ??
RemoveWindowSubclass(hwnd, Decimalni, 0);
break;
}
return DefSubclassProc(hwnd, message, wParam, lParam);
}
// dialog box procedure
INT_PTR CALLBACK DlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch (Message)
{
case WM_INITDIALOG:
{
// subclass edit control
SmartPointer<LocaleInfo> *li = (SmartPointer<LocaleInfo>*)lParam;
SetWindowSubclass(GetDlgItem(hwnd, IDC_EDIT1),
Decimalni, 0, (DWORD_PTR)li);
SetWindowSubclass(GetDlgItem(hwnd, IDC_EDIT2),
Decimalni, 0, (DWORD_PTR)li);
}
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
case IDCANCEL:
EndDialog(hwnd, IDCANCEL);
break;
}
break;
default:
return FALSE;
}
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static SmartPointer<LocaleInfo> li; // store decimal separator and minus sign
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);
/*** get information for decimal separator and negative sign ***/
li = SmartPointer<LocaleInfo>(LocaleInfo());
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
li->DecimalSeparator,
sizeof(li->DecimalSeparator) / sizeof(li->DecimalSeparator[0]));
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SNEGATIVESIGN,
li->NegativeSign,
sizeof(li->NegativeSign) / sizeof(li->NegativeSign[0]));
/*** create a button that launches a test dialog box *****/
HWND hButton = CreateWindowEx(0, L"Button",
L"Test subclassed edit controls in dialog box",
WS_BORDER | WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
50, 50, 350, 80, hwnd, (HMENU)8001,
GetModuleHandle(NULL), NULL);
}
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);
/** update information for decimal separator and negative sign ***/
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
li->DecimalSeparator,
sizeof(li->DecimalSeparator) / sizeof(li->DecimalSeparator[0]));
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SNEGATIVESIGN,
li->NegativeSign,
sizeof(li->NegativeSign) / sizeof(li->NegativeSign[0]));
return 0L;
}
else
break;
case WM_COMMAND:
{
if (LOWORD(wParam) == 8001)
{
DialogBoxParam(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDD_DIALOG1), hwnd, DlgProc, (LPARAM)li);
}
}
return 0L;
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;
}
If I were you, I would want to pass a pointer (preferably a smart pointer, it can be from STD or custom made) structure trough SetWindowSubclass()
or CreateDialogParam()
. This way it would reflect on changes you make inside your structure across all of your windows. Some short program would be nice, as I don't have any dialog template nearby so I can't really give you an example on how to pass it to functions.
I think that removing sublassing on WM_CLOSE should work just fine.
A little more information would be good.
struct locale_structure
{
wchar_t NegativeSign;
wchar_t separator;
};
static SmartPointer<locale_structure> MyStruct;
#ifndef __SMART_POINTER
#define __SMART_POINTER
#include <wtypes.h>
#include <utility>
template <class Type>
class SmartPointer
{
private:
void SmartDelete()
{
if (Pointer != nullptr && RefCount != nullptr && Size != nullptr)
{
if (RefCount[0] > 0)
RefCount[0]--;
else
{
if (Size[0] == 0)
delete Pointer;
else
delete[] Pointer;
delete RefCount;
delete Size;
}
Pointer = nullptr;
RefCount = nullptr;
Size = nullptr;
}
}
Type* Pointer;
size_t* Size;
UINT* RefCount;
public:
UINT size()
{
if (Pointer != NULL && RefCount != NULL && Size != NULL)
{
if (Size[0] == 0)
return 1;
else
return Size[0];
}
else
return 0;
}
SmartPointer() : Pointer(nullptr), Size(nullptr), RefCount(nullptr) {}
Type& operator*() { return *Pointer; }
const Type& operator*() const { return *Pointer; }
Type* operator->() { return Pointer; }
const Type* operator->() const { return Pointer; }
Type& operator[](size_t index) { return Pointer[index]; }
const Type& operator[](size_t index) const { return Pointer[index]; }
template<typename ConversionType>
operator ConversionType() { return reinterpret_cast<ConversionType>(this); }
SmartPointer(Type& Object, size_t Size = 0)
{
RefCount = new UINT();
this->Size = new size_t(Size);
if (Size == 0)
Pointer = new Type(std::move(Object));
else
{
Pointer = new Type[Size];
Pointer[0] = std::move(Object);
for (size_t i = 1; i < Size; i++)
Pointer[i] = Pointer[0];
}
}
~SmartPointer() { SmartDelete(); }
SmartPointer(SmartPointer &&OtherSP)
{
if (Pointer != OtherSP.Pointer)
{
SmartDelete();
Size = OtherSP.Size;
Pointer = OtherSP.Pointer;
RefCount = OtherSP.RefCount;
OtherSP.Size = nullptr;
OtherSP.Pointer = nullptr;
OtherSP.RefCount = nullptr;
}
}
SmartPointer& operator=(SmartPointer &&OtherSP)
{
if (Pointer != OtherSP.Pointer)
{
SmartDelete();
Size = OtherSP.Size;
Pointer = OtherSP.Pointer;
RefCount = OtherSP.RefCount;
OtherSP.Size = nullptr;
OtherSP.Pointer = nullptr;
OtherSP.RefCount = nullptr;
}
return *this;
}
SmartPointer(const SmartPointer &OtherSP)
{
if (Pointer != OtherSP.Pointer)
{
SmartDelete();
Size = OtherSP.Size;
Pointer = OtherSP.Pointer;
RefCount = OtherSP.RefCount;
RefCount[0]++;
}
else
RefCount[0]++;
}
SmartPointer& operator=(const SmartPointer &OtherSP)
{
if (Pointer != OtherSP.Pointer)
{
SmartDelete();
Size = OtherSP.Size;
Pointer = OtherSP.Pointer;
RefCount = OtherSP.RefCount;
RefCount[0]++;
}
else
RefCount[0]++;
return *this;
}
};
#endif