How to refresh registry keys without reboot? - delphi

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});

Related

Access Violation on iOS when use IFMXImageManagerService

I'm using this simple code:
procedure TForm1.Button1Click(Sender: TObject);
var servicios: IFMXImageManagerService;
i:integer;
begin
i:=servicios.GetCount;
showmessage(inttostr(i));
end;
And I get an iOS message with: "Access Violation at address 0000000104BB0460, accessing address 00000000000000000".
All that I try with IFMXImageManagerService fires that violation message.
Please, anyone know why?
Thanks!
You are not initializing servicios to point at anything meaningful, so of course calling any methods on it, like servicios.GetCount(), will fail.
You need to use TPlatformServices.GetPlatformService() or TPlatformServices.SupportsPlatformService() to initialize servicios. This is explained in Embarcadero's documentation:
FireMonkey Platform Services
To use a platform service, you must:
Add a reference to the unit where your service is declared, such as FMX.Platform, to your unit.
Call TPlatformServices.SupportsPlatformService with the target platform service as a parameter to determine whether or not the specified platform service is supported at run time.
If SupportsPlatformService returns True, use TPlatformServices.GetPlatformService to access the actual platform service, and cast the returned service appropriately. You can alternatively use SupportsPlatformService to obtain the service as well.
Try this:
uses
..., FMX.Platform, FMX.MediaLibrary;
procedure TForm1.Button1Click(Sender: TObject);
var
servicios: IFMXImageManagerService;
i: integer;
begin
if TPlatformServices.Current.SupportsPlatformService(IFMXImageManagerService, IInterface(servicios)) then
begin
i := servicios.GetCount;
ShowMessage(IntToStr(i));
end else
ShowMessage('Image Manager not supported');
end;

Delphi adding to registry failed

I am trying to add values to Internet Explorer's Registry key from an addon. My understanding is if OpenKey() doesn't find the Registry key, then it creates the key because of the true parameter I am using. But it's not being created, and the function returns false. Any idea what I'm doing wrong?
procedure DoInitialization;
var
...
reg1: TRegistry;
begin
reg1 := tRegistry.Create(KEY_ALL_ACCESS);
try
if reg1.OpenKey('HKEY_CURRENT_USER\SOFTWARE\Microsoft\Internet Explorer\Low Rights\ElevationPolicy\{B93642D4-0A6D-11DF-AD36-FF4756D89593}', true) then begin
reg1.WriteString('AppPath', ClientDir);
reg1.WriteInteger('Policy', $00000003);
reg1.WriteString('AppName', 'xxxxxxxx.exe');
end else
ShowMessage('False');
finally
reg1.CloseKey;
end;
...
end;
The root key is to be set in the RootKey property, not the key.
reg1.RootKey := HKEY_CURRENT_USER;
if reg1.OpenKey('Software\...', True) then begin
....
In fact, HKEY_CURRENT_USER is the default so strictly speaking you don't need to set it. But it is, in my opinion, helpful to be explicit.
If that fails then likely you have got a mistake in the registry key string, or perhaps the user does not have sufficient rights. Use the LastError property of reg1 to find out why the call failed.
Note that you leak reg1. You need to destroy the object in the finally block.
DON'T use KEY_ALL_ACCESS, that requires admin rights to use. In this situation, you are just writing values to the key, so all you need to use is KEY_SET_VALUE instead. Don't request more rights than you actually need.
Also, you need to use the RootKey property to specify HKEY_CURRENT_USER, do not include it in the key path string.
And, you are leaking the TRegistry object.
Try this instead:
procedure DoInitialization;
var
...
reg1: TRegistry;
begin
reg1 := TRegistry.Create(KEY_SET_VALUE);
try
reg1.RootKey := HKEY_CURRENT_USER;
if reg1.OpenKey('\SOFTWARE\Microsoft\Internet Explorer\Low Rights\ElevationPolicy\{B93642D4-0A6D-11DF-AD36-FF4756D89593}', true) then
begin
try
reg1.WriteString('AppPath', ClientDir);
reg1.WriteInteger('Policy', $00000003);
reg1.WriteString('AppName', 'xxxxxxxx.exe');
finally
reg1.CloseKey;
end;
end else
ShowMessage('False');
finally
reg1.Free;
end;
...
end;

Can't read key from registry

I heared that Windows creates a unique key for a PC which is called "MachineID". I found two locations in my registry. Only the location "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography" should be correct. I try to read the value by this function:
Function GetMaschineID:string;
var
Reg : TRegistry;
//HKEY_CURRENT_USER\Software\Microsoft\MSNMessenger = {dd239a44-fa0d-43ff-a51c-5561d3e39de3}
//HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography = a06b0ee0-b639-4f55-9972-146776bcd5e4
begin
Reg := TRegistry.Create(KEY_READ);
try
Reg.Rootkey:=HKEY_LOCAL_MACHINE; //Hauptschlüssel
//Reg.RootKey:=HKEY_CURRENT_USER;
if Reg.OpenKey('SOFTWARE\Microsoft\Cryptography\',false) then //Unterschlüssel öffnen
//if Reg.OpenKey('Software\Microsoft\MSNMessenger\',false) then //Unterschlüssel öffnen
begin
Result:=Reg.ReadString('MachineGuid');
end;
finally
Reg.Free;
end;
end;
This version results in an empty string; you see as comment the result from the registry. The second version for "hkey_Current_user" brings the expected string result.
What is wrong with my code or are parts of the registry read protected?
Possible explanation 1
For HKLM you are subject to registry redirection. You have a 32 bit process and are trying to read a key from the 64 bit view of the registry. By default, your 32 bit process is redirected to the 32 bit view, which (implementation detail) lives under Wow6432Node.
Use the KEY_WOW64_64KEY access flag to read from the 64 bit view. As detailed here: How can I read 64-bit registry key from a 32-bit process?
Possible explanation 2
Your call to OpenKey fails for keys under HKLM because you are requesting write access and standard user does not have write access to HKLM. Use OpenKeyReadOnly instead.
Other advice
At the very least you should have debugged this a bit more. Does the call to Reg.OpenKey succeed or fail? You should have debugged enough to know that. Perhaps you did but did not say. If Reg.OpenKey failed then explanation 2 is most likely. Even then, you may subsequently suffer from the other problem.
Note also that your function does not assign to the function result variable, or raise an error, if the call to Reg.OpenKey fails. I would expect that the compiler would have warned you about that.
procedure TForm1.Button1Click(Sender: TObject);
var
registry: TRegistry;
begin
Registry := TRegistry.Create(KEY_READ OR KEY_WOW64_64KEY);
try
registry.RootKey := HKEY_LOCAL_MACHINE;
if (registry.KeyExists('\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL')) and
(registry.OpenKeyReadOnly('\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL')) then
begin
showmessage(registry.ReadString('SQLEXPRESS'));
registry.CloseKey;
end
else showmessage('failed');
finally
registry.Free;
end;
end;

Delphi Detect Word version through Registry

I just added this function which determines which mailmerge method to use. It seems to work on XP and Windows 2000. Is there any reason why it wouldn't work on NT, Vista, 7 and other Windows versions? I'm thinking will there be an issue with the registry?
function GetMSOfficeVersion: String;
var Reg: TRegistry;
begin
Result := 'Office Version Not Found';
// create the registry object
Reg := TRegistry.Create;
try
// set the root key
Reg.RootKey := HKEY_LOCAL_MACHINE;
// check for Office97
if Reg.OpenKey('\SOFTWARE\Microsoft\Office\8.0', False) then
begin
Result := 'Microsoft Office97';
end;
// check for Office2000
if Reg.OpenKey('\SOFTWARE\Microsoft\Office\9.0', False) then
begin
Result := 'Microsoft Office2000';
end;
// check for OfficeXP -- not sure if this is correct
// you have to verify the key on a machine with OfficeXP
if Reg.OpenKey('\SOFTWARE\Microsoft\Office\10.0', False) then
begin
Result := 'Microsoft OfficeXP(regkey10)';
end;
// check for 11.0
if Reg.OpenKey('\SOFTWARE\Microsoft\Office\11.0', False) then
begin
Result := 'Microsoft OfficeXP(regkey11)';
end;
// check for 12
if Reg.OpenKey('\SOFTWARE\Microsoft\Office\12.0', False) then
begin
Result := 'Microsoft Office2010';
end;
finally
// make sure we free the object we created
Reg.Free;
end;
end;
Probably insufficient privileges. Try using OpenKeyReadOnly instead of OpenKey.
Aside from making sure you create the registry in read-only mode, like TOndrej suggests, you'll also want to fix the version matching in that code, as it is wrong.
Here are the right numbers for the parts where things gets shady in your code fragment:
10.0 = Office XP
11.0 = Office 2003
12.0 = Office 2007
13.0 - doesn't exist, obvious Microsoft/US numbering standards.
14.0 = Office 2010
"Is there any reason why it wouldn't work"
Yes, individual products may create a Software\Office\#.0 entry, you should be checking for a Word subkey in the specific version's key. Even then, f.i. 'Word Viewer' might have created the Word subkey which wouldn't do mail merge. If you really want to go with the registry better look for Word.Application keys in HKEY_CLASSES_ROOT. Apart from Word.Application.# keys the Word.Application key itself has a CurVer subkey.
(Previously I suggested the below but Fox's comment to the question is much better I think.)
I would directly try to create the automation object, if it fails then that version is not available, fallback to a lower version. Or sth. like:
function IsWord14: Boolean;
begin
Result := True;
try
CreateOleObject('Word.Application.14');
except on E:EOleSysError do
if E.ErrorCode = HRESULT($800401F3) then // invalid class string
Result := False
else
raise;
end;
end;

how to write a value into a created Registry in Delphi

myReg:=TRegistry.Create;
myReg.CreateKey('\sunandan123\');
//myReg.WriteString('Tile','1');
myReg.WriteString ('TileWallpaper','1') ;
This code gives an exception that i 'failed to set the value for 'TileWallpaper'. how to correct it?
Thanks
I always do it like this.
procedure TForm1.Button1Click(Sender: TObject);
var R: TRegistry;
begin
R := TRegistry.Create;
try
if not R.OpenKey('Software\CompanyName\ProductName\SubKey', True) then
RaiseLastOSError;
R.WriteString('ValueName', '1');
R.WriteString('Other Value Name', 'Some other value');
finally R.Free;
end;
end;
Calling CreateKey doesn't open the key which is why the write fails.
The easiest solution is to replace the call to CreateKey with one to OpenKey passing True for the CanCreate parameter. This will create the key if it does not already exist, and then open it for you to use in subsequent method calls.
myReg.OpenKey('\sunandan123\', True);
myReg.WriteString ('TileWallpaper', '1');
And for the sake of completeness you should include error handling, try/finally around the lifetime of myReg etc. I would also recommend that you explicitly set RootKey since at the moment you are relying on its default value of HKCU.

Resources