Modify main window's locale after user changes

2019-06-07 00:03发布

INTRODUCTION AND RELEVANT INFORMATION:

I have MS Access 2007 database which I fill using ADO.

Among other data types ( string, integer...) I also have a double.

Since I work on Windows XP and use pure Win32 API to create GUI, I collect data from edit controls with GetDlgItemText API and then I convert that text into double using _wtof_l.

PROBLEM:

Everything works well if the user sets English or Serbian ( we use European notation for decimal and group separator ) locale and then starts the program, but the problem occurs when user changes locale settings while the program is working.

Let me demonstrate this on a small example:

Let us assume that user has English locale set.

Then user starts my application.

Then user decides to change the locale ( Control Panel->Regional and Language Settings for Windows XP ) before he hits the "Save" button.

After the changes apply he then enters data and then hits "save".

The error in converting text to double must occur ( _wtof_l will now truncate 1.25 to 1 ), since my program uses default ANSI "C" locale and did not adapt it to reflect users modification.

MY EFFORTS TO SOLVE THE PROBLEM:

To prevent this I need to adapt my program to the possibility described above-I need to set my locale to the one user selected before executing query.

To do so I use message from answer to my previous question to detect when user changes settings in Control Panel->Regional and Language Options.

Then I use _wsetlocale(LC_ALL,"") to set my applications locale to the one selected by the user.

However, wrong conversion from text to decimal number described above still occurs.

This only happens when I change the locale during my program's work. If I leave locale untouched ( as 99.9% of users will ) everything seems to work fine.

To further help the community I have made a demo application that illustrates the problem. It will be presented in the APPENDIX section at the end of this post.

QUESTION:

How can I respond to WM_SETTINGCHANGE message to set my application's locale to the one currently selected by the user so my "save" button handler can perform proper conversion from string to double with _wtof_l function?

Thank you.

Best regards.

APPENDIX:

Steps to create the demo application that illustrates my problem:

1.Create default Win32 project in Visual Studio.

2.Add the following WM_CREATE handler:

case WM_CREATE:
    {
        _wsetlocale( LC_ALL, L"" ); //set current locale at window creation

        INITCOMMONCONTROLSEX iccex;
        memset( &iccex, 0, sizeof(INITCOMMONCONTROLSEX) );
        iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
        iccex.dwICC = ICC_STANDARD_CLASSES | ICC_TAB_CLASSES | ICC_BAR_CLASSES;
        InitCommonControlsEx( &iccex );

        // text

        HWND hEdit = CreateWindowEx( 0, L"Edit", L"", 
                         WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
                         50, 50, 150, 25, hWnd, (HMENU)8002, hInst, 0 );

        // decimal number

        HWND hEdit1 = CreateWindowEx( 0, L"Edit", L"", 
                         WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
                         250, 50, 150, 25, hWnd, (HMENU)8003, hInst, 0 );

        HWND hButton = CreateWindowEx( 0, L"Button", L"Save", 
                         WS_CHILD | WS_VISIBLE | WS_BORDER | BS_PUSHBUTTON,
                         50, 150, 150, 25, hWnd, (HMENU)8004, hInst, 0 );

        SendMessage( hEdit, EM_LIMITTEXT, (WPARAM)9, (LPARAM)0 );
        SendMessage( hEdit1, EM_LIMITTEXT, (WPARAM)4, (LPARAM)0 );

    }
    return 0L;

3.Add the following handler to detect when user changes locale settings

case WM_SETTINGCHANGE:
    if( !wParam && !wcscmp( (wchar_t*)lParam, L"intl" ) )
    {
        _wsetlocale( LC_ALL, L"" ); //set it to current locale
        return 0L; // "say" we handled it
    }
    else
        break; // pass it to default window procedure

4.In WM_COMMAND handler add the following cases:

case 8004:
    {
        // initialize COM
        HRESULT hr = CoInitialize(NULL);

        // format connection string
        wchar_t *bstrConnect= L"Provider=Microsoft.ACE.OLEDB.12.0; \
            Data Source = .\\test.accdb";

        try
        {

            ADODB::_ConnectionPtr pConn("ADODB.Connection");

            hr = pConn->Open(bstrConnect, L"admin", L"", ADODB::adConnectUnspecified);

            if ( SUCCEEDED(hr) )
            {
                wchar_t text[10], number[5];

                memset( &text, L'\0', sizeof(text) );
                memset( &number, L'\0', sizeof(number) );

                GetDlgItemText( hWnd, 8002, text, 10 );    // text
                GetDlgItemText( hWnd, 8003, number, 5 );   // double

                ADODB::_CommandPtr pCmd("ADODB.Command");

                pCmd->ActiveConnection = pConn;
                pCmd->CommandText = L" insert into MyTable ( field1, field2 ) values ( ?, ? );";
                pCmd->Parameters->Append( pCmd->CreateParameter( "?", ADODB::adDouble,
                                     ADODB::adParamInput, sizeof(double),
                                    _wtof_l( number, _get_current_locale() ) ) );
               pCmd->Parameters->Append( pCmd->CreateParameter( "?",
                                    ADODB::adVarWChar, ADODB::adParamInput, 
                                    wcslen(text), text ) );
               pCmd->Execute( NULL, NULL, ADODB::adCmdText );

               pConn->Close(); // close connection

               CoUninitialize(); // uninitialize COM
            }
            else
                throw _com_error(hr); //something failed-report it
        }
        catch(_com_error& e)
        {
            MessageBox(hWnd, (LPWSTR)(e.Description()), L"Error", MB_OK | 
                MB_ICONERROR );

            CoUninitialize();
        }

     }
     break;

5.Create MS Access 2007 database in the project folder.

6.Create table MyTable and add 2 fields field1 as TEXT field, and add field2 which is double.

1条回答
劳资没心,怎么记你
2楼-- · 2019-06-07 00:39

The problem was in improper setting of the current locale.

I was using the wrong function to do that.

I do not wish to steal other people's credits so here is the link where I got the solution from.

This way, any time user changes local settings in Control Panel, my application will be able to properly convert string to double and my INSERT queries will not have errors!

Hopefully this will help others with the same problem.

查看更多
登录 后发表回答