Dave Nottage 557 Posted November 13, 2019 Going (more) insane here.. I have the following test code: uses System.Win.Registry; function SetTokenPrivilege(const APrivilege: string; const AEnable: Boolean): Boolean; var LToken: THandle; LTokenPriv: TOKEN_PRIVILEGES; LPrevTokenPriv: TOKEN_PRIVILEGES; LLength: Cardinal; LErrval: Cardinal; begin Result := False; if OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, LToken) then try // Get the locally unique identifier (LUID) . if LookupPrivilegeValue(nil, PChar(APrivilege), LTokenPriv.Privileges[0].Luid) then begin LTokenPriv.PrivilegeCount := 1; // one privilege to set case AEnable of True: LTokenPriv.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; False: LTokenPriv.Privileges[0].Attributes := 0; end; LPrevTokenPriv := LTokenPriv; // Enable or disable the privilege Result := AdjustTokenPrivileges(LToken, False, LTokenPriv, SizeOf(LPrevTokenPriv), LPrevTokenPriv, LLength); end; finally CloseHandle(LToken); end; end; procedure TForm1.Button1Click(Sender: TObject); var LReg: TRegistry; begin if not SetTokenPrivilege('SeBackupPrivilege', True) then Exit; // <====== LReg := TRegistry.Create(KEY_ALL_ACCESS); try LReg.RootKey := HKEY_CURRENT_USER; if not LReg.SaveKey('Software\Microsoft\Notepad', 'C:\Temp\Notepad.reg') then ShowMessage('Could not save'); finally LReg.Free; end; end; Which results in showing the "Could not save" message. Am I missing something obvious? Share this post Link to post
FredS 138 Posted November 13, 2019 (edited) 56 minutes ago, Dave Nottage said: "Could not save" Works in Berlin when I change output to an existing folder: 'd:\Notepad.dat' Plus this addition gives me the dialog below if I try to save into a dir which does not exist: ShowMessage('Could not save: ' + LReg.LastErrorMsg); [Window Title] Project1 [Content] Could not save: The system cannot find the path specified [OK] Edited November 13, 2019 by FredS Share this post Link to post
Dave Nottage 557 Posted November 13, 2019 (edited) 2 hours ago, FredS said: Could not save: The system cannot find the path specified I receive: [Window Title] Project1 [Content] Could not save: A required privilege is not held by the client [OK] Which is odd, since SetTokenPrivilege succeeds. Using Delphi 10.3.2 Edited November 13, 2019 by Dave Nottage Share this post Link to post
Rollo62 536 Posted November 13, 2019 (edited) Stupid question: Does Temp really exist (typo, wrong UTF8-character, etc.) ? I usually put a FileExists before such things, to be sure. 'C:\Temp\Notepad.reg' Edited November 13, 2019 by Rollo62 Share this post Link to post
FredS 138 Posted November 13, 2019 12 hours ago, Dave Nottage said: A required privilege is not held by the client I was using Berlin elevated when I ran that and get the same error msg while running non elevated. My own wrapper around AdjustTokenPrivileges works because it checks the last error after that call regardless of the number of privileges adjusted. In your demo (GetLastError=1300) while running non elevated. This code behaves correctly: // Enable or disable the privilege Result := AdjustTokenPrivileges(LToken, False, LTokenPriv, SizeOf(LPrevTokenPriv), LPrevTokenPriv, LLength); Result := Result and (GetLastError = ERROR_SUCCESS); Share this post Link to post
Remy Lebeau 1394 Posted November 13, 2019 (edited) 12 hours ago, Dave Nottage said: Could not save: A required privilege is not held by the client Which is odd, since SetTokenPrivilege succeeds. Per the AdjustTokenPrivileges() documentation: Quote If the function succeeds, the return value is nonzero. To determine whether the function adjusted all of the specified privileges, call GetLastError, which returns one of the following values when the function succeeds: Return code Description ERROR_SUCCESS The function adjusted all specified privileges. ERROR_NOT_ALL_ASSIGNED The token does not have one or more of the privileges specified in the NewState parameter. The function may succeed with this error value even if no privileges were adjusted. The PreviousState parameter indicates the privileges that were adjusted. Your code is not differentiating between ERROR_SUCCESS and ERROR_NOT_ALL_ASSIGNED if AdjustTokenPrivileges() returns non-zero. Edited November 13, 2019 by Remy Lebeau Share this post Link to post
Remy Lebeau 1394 Posted November 13, 2019 11 hours ago, Rollo62 said: Stupid question: Does Temp really exist (typo, wrong UTF8-character, etc.) ? I usually put a FileExists before such things, to be sure. 'C:\Temp\Notepad.reg' TRegistry.SaveKey() uses the Win32 API RegSaveKey() function, which fails if the output file already exists, per documented behavior: Quote lpFile The name of the file in which the specified key and subkeys are to be saved. If the file already exists, the function fails. Perhaps you meant DirectoryExists() instead of FileExists()? You could always call ForceDirectories() before SaveKey(), just in case. 1 Share this post Link to post
FredS 138 Posted November 13, 2019 (edited) 2 hours ago, Remy Lebeau said: Your code is not differentiating between Which should not be required for a single privilege. Edited November 13, 2019 by FredS Removed, ended up not working. 1 Share this post Link to post
Anders Melander 1782 Posted November 13, 2019 3 hours ago, Remy Lebeau said: TRegistry.SaveKey() uses the Win32 API RegSaveKey() function Additionally, RegSaveKey doesn't save in .reg format but in registry hive format. There is no API for saving in .reg format. 1 Share this post Link to post
Remy Lebeau 1394 Posted November 14, 2019 6 hours ago, FredS said: Which should not be required for a single privilege. Enabling/disabling even a single privilege can still potentially report ERROR_NOT_ALL_ASSIGNED. It doesn't hurt to handle that condition, just in case. 1 Share this post Link to post
Joseph MItzen 251 Posted November 15, 2019 On 11/13/2019 at 12:44 PM, Remy Lebeau said: ERROR_SUCCESS OK, that is the worst name for a return value I've ever encountered! Share this post Link to post
Turan Can 3 Posted February 6, 2020 (edited) Have you tried to open your exe with administrator mode? Edited February 6, 2020 by Turan Can Share this post Link to post
Turan Can 3 Posted February 6, 2020 Hi Dave, I prepared a sample that works for you. a few days ago you solved my problem. now it's my turn 😉 uses System.Win.Registry; function SetPrivilege(Privilege: PChar; EnablePrivilege: Boolean; out PreviousState: Boolean): DWORD; var Token: THandle; NewState: TTokenPrivileges; Luid: TLargeInteger; PrevState: TTokenPrivileges; Return: DWORD; begin PreviousState := True; if (GetVersion() > $80000000) then // Win9x Result := ERROR_SUCCESS else begin // WinNT if not OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, Token) then Result := GetLastError() else try if not LookupPrivilegeValue(nil, Privilege, Luid) then Result := GetLastError() else begin NewState.PrivilegeCount := 1; NewState.Privileges[0].Luid := Luid; if EnablePrivilege then NewState.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED else NewState.Privileges[0].Attributes := 0; if not AdjustTokenPrivileges(Token, False, NewState, SizeOf(TTokenPrivileges), PrevState, Return) then Result := GetLastError() else begin Result := ERROR_SUCCESS; PreviousState := (PrevState.Privileges[0].Attributes and SE_PRIVILEGE_ENABLED <> 0); end; end; finally CloseHandle(Token); end; end; end; function RegSaveKeyToFile(Key: HKEY; const SubKey, FileName: string): DWORD; const SE_BACKUP_NAME = 'SeBackupPrivilege'; var PreviousState: Boolean; KeyHandle: HKEY; begin Result := SetPrivilege(SE_BACKUP_NAME, True, PreviousState); if (Result = ERROR_SUCCESS) then try KeyHandle := 0; Result := RegOpenKeyEx(Key, PChar(SubKey), 0, MAXIMUM_ALLOWED, KeyHandle); if (Result = ERROR_SUCCESS) then try // FIXME: Short Filename on Win9x! Result := RegSaveKey(KeyHandle, PChar(FileName), nil); finally RegCloseKey(KeyHandle); end; finally if (not PreviousState) then SetPrivilege(SE_BACKUP_NAME, PreviousState, PreviousState); end; end; function RegLoadKeyFromFile(Key: HKEY; const SubKey, FileName: string): DWORD; const SE_BACKUP_NAME = 'SeBackupPrivilege'; SE_RESTORE_NAME = 'SeRestorePrivilege'; var PrevBackup: Boolean; PrevRestore: Boolean; KeyHandle: HKEY; ShortName: array [0 .. MAX_PATH] of Char; begin Result := SetPrivilege(SE_BACKUP_NAME, True, PrevBackup); if (Result = ERROR_SUCCESS) then try Result := SetPrivilege(SE_RESTORE_NAME, True, PrevRestore); if (Result = ERROR_SUCCESS) then try if (GetVersion() > $80000000) then begin // Win9x (FIXME: Test it! - and see RegReplaceKey) if (GetShortPathName(PChar(FileName), ShortName, MAX_PATH) = 0) then Result := GetLastError() else Result := RegLoadKey(Key, PChar(SubKey), ShortName); end else begin // WinNT (FIXME: Load RegRestoreKey dynamically!) KeyHandle := 0; Result := RegOpenKeyEx(Key, PChar(SubKey), 0, MAXIMUM_ALLOWED, KeyHandle); if (Result = ERROR_SUCCESS) then try Result := RegRestoreKey(KeyHandle, PChar(FileName), 0); finally RegCloseKey(KeyHandle); end; end; finally if (not PrevRestore) then SetPrivilege(SE_RESTORE_NAME, PrevRestore, PrevRestore); end; finally if (not PrevBackup) then SetPrivilege(SE_BACKUP_NAME, PrevBackup, PrevBackup); end; end; procedure TForm1.Button1Click(Sender: TObject); const Key = HKEY(HKEY_LOCAL_MACHINE); SubKey = 'Software\Microsoft\Notepad'; var FileName: string; ErrorCode: DWORD; begin SetLength(FileName, MAX_PATH + 1); SetLength(FileName, GetTempPath(MAX_PATH, PChar(FileName))); FileName := 'C:\build\notepad.reg'; ErrorCode := RegSaveKeyToFile(Key, SubKey, FileName); if (ErrorCode <> ERROR_SUCCESS) then ShowMessage('Save: ' + SysErrorMessage(ErrorCode)) else begin ErrorCode := RegLoadKeyFromFile(Key, SubKey, FileName); if (ErrorCode <> ERROR_SUCCESS) then ShowMessage('Load: ' + SysErrorMessage(ErrorCode)) else ShowMessage(IntToStr(42)); end; end; Share this post Link to post
Anders Melander 1782 Posted February 6, 2020 30 minutes ago, Turan Can said: FileName := 'C:\build\notepad.reg'; ErrorCode := RegSaveKeyToFile(Key, SubKey, FileName); RegSaveKey still doesn't save in .reg format. Didn't you read my previous comment? Share this post Link to post