如何在不重启的情况下刷新注册表项?

How to refresh registry keys without reboot?

我使用以下代码更改注册表中的区域数据。

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;

但这需要在更改生效之前重新启动机器。不重启也可以吗?

更改注册表后,广播系统范围的 WM_SETTINGCHANGE message by calling SendMessageTimeout(),其 hWnd 设置为 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".

例如:

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;

在你问之前,是的,以这种方式在最后一个参数中使用 nil 是安全的:

Passing nil to a variable parameter

在 XE2 之前,Delphi 的 Windows 单元将 SendMessageTimeout() 的最后一个参数声明为:

var lpdwResult: DWORD

但 Win32 API 将参数定义为:

_Out_opt_ PDWORD_PTR lpdwResult

允许传入 NULL 指针。上述 nil 技巧是 Delphi 代码将 NULL 值传递给 var 参数的唯一方法。编译器生成的机器代码是正确的——它只是简单地将值 0 传递给参数,它实际上不会尝试访问内存地址 $00000000.

在 XE2 中,Windows 单元被更改为将最后一个参数声明为:

lpdwResult: PDWORD_PTR

匹配 Win32 API 定义。

因此,如果您将代码升级到 XE2 或更高版本,只需将 PDWORD(nil)^ 替换为 nil。或者,您可以现在考虑,以后不用担心:

SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, LPARAM(PChar('intl')), SMTO_NORMAL, 100, {$IF RTLVersion >= 23}nil{$ELSE}PDWORD(nil)^{$IFEND});