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
.
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
todouble
and myINSERT
queries will not have errors!Hopefully this will help others with the same problem.