Запустите мою программу от имени пользователя - PullRequest
2 голосов
/ 21 июля 2010

Windows 7, Vista, Server 2008, UAC активирован

Для выполнения некоторых действий по установке необходимо указать программу с правами администратора. После этого я хочу, чтобы моя программа продолжала работать без прав администратора.

Как я могу перезапустить его без прав администратора?


приписка

Моя программа переустанавливает себя. Я не хочу распространять какие-либо дополнительные программы для этого. Итак, мои шаги:

  1. Скачать новую версию в temp dir
  2. Перезагрузить себя с правами администратора
  3. Переименуйте старый exe-файл и скопируйте новый exe-файл из временного каталога
  4. Перезапустить себя без прав администратора

Ответы [ 4 ]

4 голосов
/ 22 июля 2010

Спасибо Кейт Грегори за помощь.

На Delphi есть рабочий код:

function RunAsUser(CommandLine, WorkDirectory: string; Wait: Boolean): Boolean;
const
  TOKEN_ADJUST_SESSIONID = $0100;
  dwTokenRights = TOKEN_QUERY or TOKEN_ASSIGN_PRIMARY or TOKEN_DUPLICATE or TOKEN_ADJUST_DEFAULT or TOKEN_ADJUST_SESSIONID;
var
  WExe, WCmdLine, wCurrDir: WideString;
  hProcessToken, dwLastErr, retLength, hwnd, dwPID, hShellProcess, hShellProcessToken, hPrimaryToken: Cardinal;
  tkp: TOKEN_PRIVILEGES;
  PI: TProcessInformation;
  SI: TStartupInfoW;
begin
  Result:= False;

  hShellProcessToken:= 0;
  hPrimaryToken:= 0;
  hShellProcess:= 0;

  if WorkDirectory = '' then WorkDirectory:= GetCurrentDir;
  Wexe:= SeparateText(CommandLine, ' ');
  WCmdLine:= CommandLine;
  wCurrDir:= WorkDirectory;

    // Enable SeIncreaseQuotaPrivilege in this process.  (This won't work if current process is not elevated.)
    if not OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, hProcessToken) then Exit;

  tkp.PrivilegeCount:= 1;
  LookupPrivilegeValueW(nil, SE_INCREASE_QUOTA_NAME, tkp.Privileges[0].Luid);
  tkp.Privileges[0].Attributes:= SE_PRIVILEGE_ENABLED;
  AdjustTokenPrivileges(hProcessToken, FALSE, tkp, 0, nil, retLength);
  dwLastErr:= GetLastError();
  CloseHandle(hProcessToken);
  if (dwLastErr <> ERROR_SUCCESS) then Exit;

    // Get an HWND representing the desktop shell.
    // CAVEATS:  This will fail if the shell is not running (crashed or terminated), or the default shell has been
    // replaced with a custom shell.  This also won't return what you probably want if Explorer has been terminated and
    // restarted elevated.

    hwnd:= GetShellWindow();
  if hwnd = 0 then Exit;

  // Get the PID of the desktop shell process.
  GetWindowThreadProcessId(hwnd, dwPID);
  if dwPID = 0 then Exit;

  // Open the desktop shell process in order to query it (get the token)
  hShellProcess:= OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwPID);
  if hShellProcess = 0 then Exit;

  // From this point down, we have handles to close, so make sure to clean up.
  try
    // Get the process token of the desktop shell.
    if not OpenProcessToken(hShellProcess, TOKEN_DUPLICATE, hShellProcessToken) then Exit;

    // Duplicate the shell's process token to get a primary token.
    // Based on experimentation, this is the minimal set of rights required for CreateProcessWithTokenW (contrary to current documentation).
    if not DuplicateTokenEx(hShellProcessToken, dwTokenRights, nil, SecurityImpersonation, TokenPrimary, hPrimaryToken) then Exit;

    SI.cb:= SizeOf(SI);
    FillChar(SI, SI.cb, 0);
    SI.wShowWindow:= SW_SHOWNORMAL;
    SI.dwFlags:= STARTF_USESHOWWINDOW;

    // Start the target process with the new token.
    Result:= CreateProcessWithTokenW(
      hPrimaryToken,
      0,
      PWideChar(WExe),
      PWideChar(wCmdLine),
      0,
      nil,
      PWideChar(wCurrDir),
      @si,
      @pi);

    if not Result then Exit;

    if Wait then
      while MsgWaitForMultipleObjects(1, PI.hProcess, False, INFINITE, QS_ALLINPUT) <> WAIT_OBJECT_0 do
        ProcessMessages;

    CloseHandle(PI.hProcess);
  finally
    // Clean up resources
    CloseHandle(hShellProcessToken);
      CloseHandle(hPrimaryToken);
    CloseHandle(hShellProcess);
  end;
end;
4 голосов
/ 21 июля 2010

Под UAC делать что-либо «при первом запуске» теперь настоятельно не рекомендуется.Кроме того, программы, которые обновляют себя, используя метод «по-своему», будут более трудными.Вы говорите, что не хотите распространять дополнительные программы, но под UAC у вас действительно мало выбора.Либо все ваше приложение запускается с повышенными правами каждый раз (раздражает пользователя) на тот случай, если ему понадобится что-то административное, либо вы разбиваете его на две части и время от времени запускаете одну повышенную, а другую - не повышенную все время.

Один из способов разделить это - написать установщик, который повышает уровень, и обычное приложение, которое этого не делает.Это работает для людей, которые устанавливают один раз, делают некоторые вещи при первом запуске (вы перемещаете эти вещи в установщик), а затем выполняете.Вы говорите, что ваше приложение обновляется само.Таким образом, вам нужно переместить этот код в отдельный exe-файл и поместить манифест в этот exe-файл, в котором есть requireAdministrator.Затем ваше основное приложение запустит (используя ShellExecute) исполняемый файл обновления, когда будет доступно новое обновление.

1 голос
/ 21 июля 2010

Я думаю, вы идете по этому пути неправильно. На мой взгляд, вы должны сделать одно из следующих действий:

  • Выполнить действия по установке во время установки программного обеспечения и требовать, чтобы установка имела права администратора

или

  • Начните как не администратор и запрашивайте повышение прав, когда вам нужно выполнить некоторые действия. Таким образом, вам не нужно перезапускать программу.

Edit: Таким образом, шаги будут:

  1. Проверьте наличие новой версии и при необходимости загрузите
  2. Оповещение пользователя о наличии новой версии и запрос на повышение прав
  3. Переименовать / скопировать действие
  4. нормально перезагружается

Для запроса повышения не требуется перезапуск. Возможно, вы захотите использовать этот способ при работе в средах, предшествующих Vista.

0 голосов
/ 21 июля 2010

Вот простой метод перезапуска;

procedure Restart(RunAs: Boolean);
var
  i: Integer;
  Params: string;
begin
// Close handle to Mutex or any such thing if only one inst. is allowed

// Prepare to re-pass parameters if the application uses them
  Params := '';
  for i := 1 to ParamCount do
    Params := Params + ' "' + ParamStr(i) + '"';

  Application.MainForm.Close;
  Application.ProcessMessages;
  if RunAs then
    ShellExecute(0, 'runas', PChar(ParamStr(0)), PChar(Params), '', SW_SHOW)
  else
    ShellExecute(0, 'open', PChar(ParamStr(0)), PChar(Params), '', SW_SHOW);
end;
...