質問

INTRODUCTION AND RELEVANT INFORMATION:

I have multiple edit controls 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 controls.

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.

PROBLEM:

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.

MY EFFORTS TO SOLVE THIS:

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( ... );
    }  

QUESTIONS:

  1. How should I pass MyStruct to the CreateDialogParam and SetWindowSubclass ?

  2. How should I remove subclassing so I can avoid memory leaks?

  3. I assume that some memory allocating and deallocating will occur in subclassing procedure as well, can you instruct me how to do this properly?

EDITED ON FEBRUARY 22nd, 2014

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;
}

END OF EDIT

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;
}

  1. 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.

  2. I think that removing sublassing on WM_CLOSE should work just fine.

  3. 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
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top