I'd like to ask if anyone knows correct way how to load ntuser.dat file into registry on Windows7 or XP. I've been trying to accomplish that by using this code
_Reg := TRegistry.Create;
_Reg.RootKey := HKEY_USERS;
if (_Reg.Loadkey('Test2', 'C:\Users\Test2\NTUSER.DAT')) then
ShowMessage('User hive loaded');
_Reg.Unloadkey('Test2');
_Reg.CloseKey;
_Reg.Free;
Path to a file is correct and file exists. I've also tried to use function I found on the internet EnableNTPrivilege('SeRestorePrivilege', SE_PRIVILEGE_ENABLED) for setting up privileges before trying to load a hive. And still I get false from _Reg.Loadkey.
I've also tried to use this function NTSetPrivilege('SeRestorePrivilege', True) for setting up privileges. But this function returns error 'Not all privileges or groups referenced are assigned to the caller'
The TRegistry.LoadKey function internally uses the RegLoadKey function which requieres that the calling process have the SE_RESTORE_NAME and SE_BACKUP_NAME privileges. If the call to the function returns a value <> to ERROR_SUCCESS (0) you must check the LastError and the LastErrorMsg properties to get more info.
Check this sample to see how the function must be called and how the result of the operation is handled.
var
Reg : TRegistry;
begin
Reg := TRegistry.Create;
try
NTSetPrivilege('SeRestorePrivilege', True); //this is a third-party function, you can implemnt your own to set the privileges.
NTSetPrivilege('SeBackupPrivilege', True);
Reg.RootKey := HKEY_USERS;
if (Reg.Loadkey('Test2', 'C:\Users\Test2\NTUSER.DAT')) then
begin
try
Reg.OpenKey('Test2', False);
try
//do your stuff here
finally
Reg.CloseKey;
end;
finally
Reg.Unloadkey('Test2');
end;
end
else
Writeln(Reg.LastErrorMsg);
finally
Reg.Free;
end;
end;
The correct way to load and manipulate a specific user's Registry hive is to having your calling thread impersonate that user (if the thread is not already running as that user) and then use RegOpenCurrentUser(), RegOpenUserClassesRoot(), and/or LoadUserProfile() as needed.
Related
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;
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;
In my program, the user completes a form and then presses Submit. Then, a textfile or a random extension file is created, in which all the user's information is written. So, whenever the user runs the application form, it will check if the file, which has all the information, exists, then it copies the information and pastes it to the form. However, it is not working for some reason (no syntax errors):
procedure TForm1.FormCreate(Sender: TObject);
var
filedest: string;
f: TextFile;
info: array[1..12] of string;
begin
filedest := ExtractFilePath(ParamStr(0)) + 'User\Identity\IdentityofMyself.txt';
if FileExists(filedest) then
begin
AssignFile(f,filedest);
Reset(f);
ReadLn(info[1], info[2], info[3], info[4], info[5], info[6], info[7],
info[8], info[9], info[10], info[11], info[12]);
Edit1.Text := info[1];
Edit2.Text := info[2];
ComboBox1.Text := info[3];
ComboBox5.Text := info[4];
ComboBox8.Text := info[4];
ComboBox6.Text := info[5];
ComboBox7.Text := info[6];
Edit3.Text := info[7];
Edit4.Text := info[8];
Edit5.Text := info[11];
Edit6.Text := info[12];
ComboBox9.Text := info[9];
ComboBox10.Text := info[10];
CloseFile(f);
end
else
begin
ShowMessage('File not found');
end;
end;
The file exists, but it shows the message File not found. I don't understand.
I took the liberty of formatting the code for you. Do you see the difference (before, after)? Also, if I were you, I would name the controls better. Instead of Edit1, Edit2, Edit3 etc. you could use eFirstName, eLastName, eEmailAddr, etc. Otherwise it will become a PITA to maintain the code, and you will be likely to confuse e.g. ComboBox7 with ComboBox4.
One concrete problem with your code is this line:
readln(info[1], info[2], info[3], info[4], info[5], info[6], info[7],
info[8], info[9], info[10], info[11], info[12]);
You forgot to specify the file f!
Also, before I formatted your code, the final end of the procedure was missing. Maybe your blocks are incorrect in your actual code, so that ShowMessage will be displayed even if the file exists? (Yet another reason to format your code properly...)
If I encountered this problem and wanted to do some quick debugging, I'd insert
ShowMessage(BoolToStr(FileExists(filedest), true));
Exit;
just after the line
filedest := ...
just to see what the returned value of FileExists(filedest) is. (Of course, you could also set a breakpoint and use the debugger.)
If you get false, you probably wonder what in the world filedest actually contains: Well, replace the 'debugging code' above with this one:
ShowMessage(filedest);
Exit;
Then use Windows Explorer (or better yet: the command prompt) to see if the file really is there or not.
I'd like to mention an another possibility to output a debug message (assuming we do not know how to operate real debugger yet):
{ ... }
filedest := ExtractFilePath(ParamStr(0)) + 'User\Identity\IdentityofMyself.txt';
AllocConsole; // create console window (uses Windows module) - required(!)
WriteLn('"' + filedest + '"'); // and output the value to verify
if FileExists(filedest) then
{ ... }
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;
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.