Title: Moving from 95/98/ME to NT/2000
Question: Putting your registry entries under HKEY_CURRENT_USER might not be the thing to do under NT or 2000.
Answer:
We started developing an application using Delphi 1 in the months just prior to the release of Windows 95; at the time, NT seemed to be completely off our radar screen.
We had used INI files for storing certain configuration parameters in Windows 3, and so when we moved to '95 and Delphi 2, we took advantage of the TRegIniFile object to move these parameters into the registry.
There are are couple of "gotcha's" in using this object. For one thing, all of the various type-specific methods - WriteInteger, etc. - actually insert values into the registry as strings, which is consistent with the way they're written to an INI file. But if you use TRegIniFile.WriteInteger to write a number, and TRegistry.ReadInteger to read it, an exception will be raised. The solution to that problem is to use TRegistry.ReadString to read the value, and pass that to StrToInt().
The more serious problem we discovered was that the fact that the default RootKey for TRegIniFile is HKEY_CURRENT_USER means that on NT/2000 systems, only the user who was logged in at the time of installation of our software could use it; when another user logged in, HKEY_CURRENT_USER wouldn't contain the necessary entries.
Of course, if you want your installation to be user-specific, that might be OK, but we didn't. So, we decided to use HKEY_LOCAL_MACHINE instead. The "SOFTWARE" key under HKEY_LOCAL_MACHINE apparently grants full control to all users, so permissions aren't a problem. For existing systems, we wanted to be able to move the existing branch of registry entries from HKEY_CURRENT_USER to HKEY_LOCAL_MACHINE.
Unfortunately, the seemingly-obvious method, MoveKey, won't move entire branches under NT/2000 as it does under 95/98 (and I presume, ME). Being stupid as well as lazy, I was willing to put more time into figuring out how to move the entire branch in one fell swoop than it would have taken to move the individual keys.
It turns out that the SaveKey and RestoreKey (NOT LoadKey) functions would do the trick, but using them under NT/2000 requires that the program secure the appropriate "Privileges" - something I'd not heard of before. Fortunately, there are examples in the MSDN library that are easily translated into Pascal. Here's the code I ultimately came up with (D5):
procedure UpdateRegistryLocation;
var
reg: TRegistry;
kee: HKEY;
priv: string;
filnam: string;
res: integer;
osv: TOSVersionInfo;
tp: TTokenPrivileges;
hToken: THandle;
luid: TLargeInteger;
len: cardinal;
begin
// find out if it's NT/2000
fillchar(osv,sizeof(osv),0);
osv.dwOSVersionInfoSize := sizeof(TOSVersionInfo);
GetVersionEx(osv);
// if so, obtain the required priviledges
if (osv.dwPlatformId = VER_PLATFORM_WIN32_NT) then begin
priv := 'SeBackupPrivilege';
if OpenProcessToken(GetCurrentProcess,TOKEN_ADJUST_PRIVILEGES,hToken)
then begin
if LookupPrivilegeValue(NIL,pchar(priv1),luid) then begin
tp.PrivilegeCount := 1;
tp.Privileges[0].Luid := luid;
tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE,tp,0,NIL,len);
res := GetLastError();
if (res ERROR_SUCCESS) then begin
ShowMessage('Ouch! Can''t get backup privilege!');
exit
end
end
end;
priv := 'SeRestorePrivilege';
if OpenProcessToken(GetCurrentProcess,TOKEN_ADJUST_PRIVILEGES,hToken)
then begin
if LookupPrivilegeValue(NIL,pchar(priv1),luid) then begin
tp.PrivilegeCount := 1;
tp.Privileges[0].Luid := luid;
tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE,tp,0,NIL,len);
res := GetLastError();
if (res ERROR_SUCCESS) then begin
ShowMessage('Ouch! Can''t get restore privilege!');
exit
end
end
end
end;
filnam := 'C:\MyTemp';
reg := TRegistry.Create;
try
reg.LazyWrite := false;
reg.RootKey := HKEY_LOCAL_MACHINE;
if (not reg.KeyExists('Software\OurCompany\OurProgram')) then begin
if FileExists(filnam) then
DeleteFile(filnam);
reg.RootKey := HKEY_CURRENT_USER;
if reg.OpenKey('Software\OurCompany\OurProgram',false) then begin
kee := reg.CurrentKey;
res := RegSaveKey(kee,pchar(filnam),NIL);
if (res = ERROR_SUCCESS) then begin
reg.CloseKey;
reg.RootKey := HKEY_LOCAL_MACHINE;
reg.OpenKey('SOFTWARE\OurCompany\OurProgram',true);
kee := reg.CurrentKey;
res := RegRestoreKey(kee,pchar(filnam),0);
reg.CloseKey;
if (res = ERROR_SUCCESS) then begin
DeleteFile(filnam);
DeleteFile(Concat(filnam,'.LOG'));
end else begin
ShowMessage('Bummer! No joy! Registry move failed!')
end
end
end
end
finally
reg.free
end
end;
Note that the name of the temporary file has no extension, as it cannot under Windows 95 - nor can you use a long filename with 95. The SaveKey function also creates a log file, hence the additional file deletion on success.
I'd like to know who at Microsoft came up with the constant name "ERROR_SUCCESS"...