以用户身份运行我的程序

发布于 2024-09-10 10:41:50 字数 390 浏览 9 评论 0原文

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

程序必须具有管理员权限才能进行某些安装操作。之后,我希望我的程序能够继续以非管理员权限运行。

如何在没有管理权限的情况下重新启动它?


PS

我的程序会自行重新安装。我不想为其分发任何附加程序。所以我的步骤是:

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

Windows 7, Vista, Server 2008, UAC is activated

Program must be stated with admin rights to make some installation actions. After that I want my program to continue work with non-admin rights.

How can I restart it with not administrative rights?


P.S.

My program reinstall itself. I don't want distribute any additional programs for it. So my steps are:

  1. Download new version in temp dir
  2. Restart itself under admin rights
  3. Rename old exe-file and copy new exe-file from temp dir
  4. Restart itself under non-admin rights

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

蹲在坟头点根烟 2024-09-17 10:41:50

在 UAC 下,现在强烈建议不要在“首次运行”时执行任何操作。此外,使用自行开发技术进行自我更新的程序会发现更加困难。您说您不想分发额外的程序,但在 UAC 下您实际上别无选择。要么你的整个应用程序每次都以提升的方式运行(惹恼用户),以防它碰巧需要执行一些管理操作,要么将其分成两部分,并偶尔运行一个提升的部分,而另一部分始终不提升。

拆分它的一种方法是编写一个安装程序,它可以提升,而常规应用程序则不能。这适用于安装一次、在第一次运行时执行一些操作(将这些操作移至安装程序)然后完成的人。你说你的应用程序会自行更新。因此,您需要将该代码移动到一个单独的 exe 中,并在该 exe 上放置一个包含 requireAdministrator 的清单。然后,当有新的更新可用时,您的主应用程序将启动(使用 ShellExecute)更新 exe。

Under UAC, doing anything "on first run" is now strongly discouraged. Also, programs that update themselves using a roll-your-own technique will find it more difficult. You say you don't want to distribute additional programs, but under UAC you really have very little choice. Either your whole app runs elevated every time (annoying the user) in case it happens to need to do something administrative, or you split it into two parts, and run one elevated occasionally and the other non elevated all the time.

One way to split it is to write an installer, which elevates, and the regular app, which doesn't. That works for the people who install once, do some things on first run (you move those things to the installer) and then are done. You say your app updates itself. So you need to move that code to a separate exe and put a manifest on that exe that has requireAdministrator. Then your main app will launch (using ShellExecute) the updating exe when there is a new update available.

┊风居住的梦幻卍 2024-09-17 10:41:50

感谢凯特·格雷戈里的帮助。

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;

Thanx to Kate Gregory for help.

There is a working code on 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;
不必了 2024-09-17 10:41:50

我认为你在这方面走错了路。我认为您应该执行以下操作之一:

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

,或者

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

编辑:
因此,步骤如下:

  1. 检查新版本并在必要时下载
  2. 提醒用户有新版本可用并请求提升 重
  3. 命名/复制操作
  4. 正常重新启动

请求提升无需重新启动。在 Vista 之前的环境中工作时,您可能仍希望使用这种方式。

I think you are going the wrong way at this. In my opinion you should do one of the following:

  • Do the installation actions during the installation of the software and require the installation to have administrator rights

or

  • Start as non-administrator and request elevation when you need to perform some actions. That way you don't have to restart the program.

Edit:
So the steps would be:

  1. Check for new version and download if necessary
  2. Alert user that a new version is available and request elevation
  3. Rename / copy action
  4. Restart normally

There is no restart necessary for requesting elevation. You might want to still use this way when working on pre-Vista environments.

如梦初醒的夏天 2024-09-17 10:41:50

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

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;

Here's a simple restart method;

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文