运行我的程序asUser

时间:2010-07-21 11:15:56

标签: c# windows delphi process uac

Windows 7,Vista,Server 2008,UAC已激活

必须使用管理员权限声明程序才能执行某些安装操作。之后,我希望我的程序继续使用非管理员权限。

如何以非管理权限重新启动它?


P.S。

我的程序重新安装。我不想为它分发任何其他程序。所以我的步骤是:

  1. 在temp dir中下载新版本
  2. 在管理员权限下重新启动
  3. 重命名旧的exe文件并从temp dir复制新的exe文件
  4. 在非管理员权限下重新启动

4 个答案:

答案 0 :(得分:4)

根据UAC,现在强烈建议不要“首次运行”。此外,使用自己动手技术更新自己的程序会发现它更加困难。你说你不想分发其他程序,但在UAC下你真的没什么选择。您的整个应用程序每次都会提升(令用户烦恼),以防它恰好需要执行某些管理操作,或者您将其分成两部分,并且偶尔运行一个提升而另一个非提升。

分割它的一种方法是编写一个升级的安装程序和不升级的常规应用程序。这适用于安装一次的人,在第一次运行时执行某些操作(将这些操作移动到安装程序)然后完成。你说你的应用程序自我更新。因此,您需要将该代码移动到单独的exe并将清单放在具有requireAdministrator的exe上。然后,当有新的更新可用时,您的主应用程序将启动(使用ShellExecute)更新exe。

答案 1 :(得分:4)

Thanx到 Kate Gregory 寻求帮助。

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;

答案 2 :(得分:1)

我认为你在这方面做错了。在我看来,你应该做以下其中一件事:

  • 在安装软件期间执行安装操作,并要求安装具有管理员权限

  • 当您需要执行某些操作时,以非管理员身份启动并请求提升。这样您就不必重新启动程序。

编辑: 所以步骤将是:

  1. 检查新版本并在必要时下载
  2. 提醒用户新版本可用且请求提升
  3. 重命名/复制操作
  4. 正常重启
  5. 请求提升不需要重新启动。在Vista之前的环境中工作时,您可能仍希望使用这种方式。

答案 3 :(得分:0)

这是一个简单的重启方法;

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;