This is quite simple to do, but as
@Fibonacci wrote, you cannot raise privileges for a current process — you have to start a new one with administrator rights. You can run another program, that is, another executable file, prepared specifically to perform this special task, but you can also launch a second instance of the same application and provide it with arguments that will allow it to perform this special task, return operation code (process exit code) and exit immediately.
In my editors, I added the ability to edit the contents of the registry. The editor itself does not require elevated privileges to run, but modifying the
HKEY_LOCAL_MACHINE branch in the registry does. So when the user chooses, for example, the option to register file extensions so that files with these extensions are run by default in my editor, the editor starts itself (with appropriate arguments), detects these arguments, modifies the registry, sets the exit code and closes — skips form creation and normal
Application handling. The main instance reads the exit code and thus knows whether the operation was successful, and if not, it displays an appropriate error message based on this code.
The code that opens a new instance with elevated privileges and waits for the task to be executed looks like this (shortened):
uses
Windows,
ShellAPI;
function TEditorUtilsInstances.OpenRegister(const AParameters: String): Integer;
var
ShellInfo: SHELLEXECUTEINFO;
Filename: String;
begin
// Prepare path for the application to run (in this case, the second instance of the editor).
Filename := FontEditor.Utils.Instances.Location;
Filename += ExtractFileName(Application.ExeName);
ShellInfo := Default(SHELLEXECUTEINFO);
ShellInfo.cbSize := SizeOf(ShellInfo);
ShellInfo.lpVerb := 'runas'; // Force displays a UAC dialog for confirmation.
ShellInfo.lpFile := PChar(Filename); // Application path to run.
ShellInfo.lpParameters := PChar(AParameters); // Arguments that allow the application to detect that it has been launched only to perform a special task.
ShellInfo.fMask := SEE_MASK_NOCLOSEPROCESS; // Wait until the process finishes.
ShellInfo.nShow := SW_SHOW;
// Display UAC window and wait for confirmation.
if ShellExecuteExA(@ShellInfo) then
begin
// The user has accepted the UAC dialog, so wait for the second instance to complete.
WaitForSingleObject(ShellInfo.hProcess, INFINITE);
// Read the process exit code containing the result of the task execution (should never fail).
if not GetExitCodeProcess(ShellInfo.hProcess, LongWord(Result)) then
Result := EDITOR_ERROR_REGISTER_SUCCESS;
// Close the process handle.
CloseHandle(ShellInfo.hProcess);
end
else
// The user cancelled the UAC dialog (pressed "no" or close button), so do nothing.
Result := EDITOR_ERROR_REGISTER_CANCELLED;
end;
The code of the main editor module looks like this:
begin
case Editor.FontEditor.Utils.Instances.OpenedReason() of
EDITOR_OPEN_REASON_LOCATION_REGISTER: ExitCode := Editor.FontEditor.Utils.Register.LocationRegister();
EDITOR_OPEN_REASON_LOCATION_UNREGISTER: ExitCode := Editor.FontEditor.Utils.Register.LocationUnregister();
EDITOR_OPEN_REASON_ASSOCIATION_REGISTER: ExitCode := Editor.FontEditor.Utils.Register.AssociationRegister();
EDITOR_OPEN_REASON_ASSOCIATION_UNREGISTER: ExitCode := Editor.FontEditor.Utils.Register.AssociationUnegister();
otherwise
Editor.FontEditor.Utils.Instances.Open();
end
end.
First, the reason for launching the application is checked based on run arguments:
function TEditorUtilsInstances.OpenedReason(): Integer;
begin
Result := EDITOR_OPEN_REASON_NORMAL;
case Application.ParamCount of
1: case Application.Params[1] of
EDITOR_PARAMETER_LOCATION_REGISTER: Result := EDITOR_OPEN_REASON_LOCATION_REGISTER;
EDITOR_PARAMETER_LOCATION_UNREGISTER: Result := EDITOR_OPEN_REASON_LOCATION_UNREGISTER;
EDITOR_PARAMETER_ASSOCIATION_REGISTER: Result := EDITOR_OPEN_REASON_ASSOCIATION_REGISTER;
EDITOR_PARAMETER_ASSOCIATION_UNREGISTER: Result := EDITOR_OPEN_REASON_ASSOCIATION_UNREGISTER;
otherwise
Result := EDITOR_OPEN_REASON_EDIT;
end;
2: case Application.Params[1] of
EDITOR_PARAMETER_OPEN_CLONED: Result := EDITOR_OPEN_REASON_CLONE;
end;
end;
end;
If the argument is one of those specifying a special task (e.g.
EDITOR_OPEN_REASON_ASSOCIATION_REGISTER, i.e. registering file extensions in the registry), the editor performs its task, sets the exit code and closes. However, if it was started normally, it creates a form and runs normally:
procedure TEditorUtilsInstances.Open();
begin
RequireDerivedFormResource := True;
Application.Title := 'Project: Kids — Font Editor';
Application.Scaled := True;
Application.Initialize();
Application.CreateForm(TFormMain, FormMain);
Application.Run();
end;