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 case
s:
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
.