I use the following code to change region data in the Registry.
procedure TForm1.Button1Click(Sender: TObject);
var
reg: TRegistry;
begin
reg:=TRegistry.Create;
try
reg.RootKey:=HKEY_CURRENT_USER;
reg.OpenKey('\Control Panel\International\',true);
reg.WriteString('iCountry','1');
reg.WriteString('iCurrDigits','2');
reg.WriteString('iCurrency','0');
reg.WriteString('iDate','1');
reg.WriteString('iDigits','2');
reg.WriteString('iLZero','0');
reg.WriteString('iMeasure','1');
reg.WriteString('iNegCurr','0');
reg.WriteString('iNegNumber','1');
reg.WriteString('iTimePrefix','0');
reg.WriteString('iTLZero','1');
reg.WriteString('Locale','00000409');
reg.WriteString('LocaleName','en-US');
reg.WriteString('sCountry','United States');
reg.WriteString('sDate','/');
reg.WriteString('sDecimal','.');
reg.WriteString('iNegCurr','0');
reg.WriteString('sShortDate','dd/MM/yyyy'); reg.CloseKey;
finally
reg.free;
end;
end;
but this requires restarting the machine before the changes take effect. Can it be done without rebooting?
After changing the Registry, broadcast a system-wide WM_SETTINGCHANGE
message by calling SendMessageTimeout()
with its hWnd
set to HWND_BROADCAST
:
Applications should send WM_SETTINGCHANGE to all top-level windows when they make changes to system parameters.
...
wParam
... When the system sends this message as a result of a change in locale settings, this parameter is zero.When an application sends this message, this parameter must be NULL.
...
lParam
... When the system sends this message as a result of a change in locale settings, this parameter points to the string "intl".
For example:
procedure TForm1.Button1Click(Sender: TObject);
var
reg: TRegistry;
begin
reg := TRegistry.Create;
try
reg.RootKey := HKEY_CURRENT_USER;
reg.Access := KEY_SET_VALUE;
if reg.OpenKey('\Control Panel\International\', true) then
try
reg.WriteString('iCountry','1');
reg.WriteString('iCurrDigits','2');
reg.WriteString('iCurrency','0');
reg.WriteString('iDate','1');
reg.WriteString('iDigits','2');
reg.WriteString('iLZero','0');
reg.WriteString('iMeasure','1');
reg.WriteString('iNegCurr','0');
reg.WriteString('iNegNumber','1');
reg.WriteString('iTimePrefix','0');
reg.WriteString('iTLZero','1');
reg.WriteString('Locale','00000409');
reg.WriteString('LocaleName','en-US');
reg.WriteString('sCountry','United States');
reg.WriteString('sDate','/');
reg.WriteString('sDecimal','.');
reg.WriteString('iNegCurr','0');
reg.WriteString('sShortDate','dd/MM/yyyy');
finally
reg.CloseKey;
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, LPARAM(PChar('intl')), SMTO_NORMAL, 100, PDWORD(nil)^);
end;
finally
reg.free;
end;
end;
And before you ask, yes it is safe to use nil
in the last parameter in this manner:
Passing nil to a variable parameter
Prior to XE2, Delphi's Windows
unit declares the last parameter of SendMessageTimeout()
as:
var lpdwResult: DWORD
But the Win32 API defines the parameter as:
_Out_opt_ PDWORD_PTR lpdwResult
Which allows a NULL pointer to be passed in. The above nil
trick is the only way for Delphi code to pass a NULL value to a var
parameter. The machine code generated by the compiler will be correct - it will simply pass a value of 0 to the parameter, it will not actually try access memory address $00000000.
In XE2, the Windows
unit was changed to declare the last parameter as:
lpdwResult: PDWORD_PTR
To match the Win32 API definition.
So, if you ever upgrade your code to XE2 or later, simply replace PDWORD(nil)^
with nil
instead. Or, you can account for it now and not worry about it later:
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, LPARAM(PChar('intl')), SMTO_NORMAL, 100, {$IF RTLVersion >= 23}nil{$ELSE}PDWORD(nil)^{$IFEND});