【问题标题】:Writing a windows program with updating, Permission problems编写带有更新的 Windows 程序,权限问题
【发布时间】:2014-03-07 02:33:17
【问题描述】:

我想创建一个程序Foo.exe,但错误登录到foo.log。该程序已安装(NSIS 安装程序)到C:\Program Files (x86)\Foo\。在安装过程中,NSIS 添加了一个快捷方式 (CreateShortCut "$SMSTARTUP\Foo.lnk" "$INSTDIR\Foo.exe") 以在 windows 启动时启动 Foo.exe。

  • 当 Windows 在启动后执行我的程序时,它会以“非管理权限”启动我的程序。所以我的程序不能编辑C:\Program Files (x86)\Foo\foo.log。解决方法是将可编辑文件存储在另一个目录中,例如在$APPDATA。
  • 但是真正的问题来了:我在我的程序中添加了一个更新功能,它的工作方式如下:
    • 下载新的二进制文件并保存为FooNew.exe。此文件没有任何权限(明确没有执行标志)。
    • 创建具有执行权限的自我副本:FooCopy.exe
    • 启动后FooCopy.exe打开FooNew.exe并将其内容写入Foo.exe。现在,Foo.exe 已更新并具有执行权限。
    • FooCopy.exe 将被删除。

所以我没有测试更新,但很可能它不会工作。我假设Foo.exe 无法在C:\Program Files (x86)\Foo\ 中创建文件,因此更新在第 1 点失败。

一个想法是将Foo.exe 保存在C:\Program Files (x86)\ 中而不是$APPDATA 中。所以该程序具有所有需要的权利。问题是启动该程序:它是为 32 位编译的。是否有可能有一个“启动器”C:\Program Files (x86)\Foo\launcher.exe 在$APPDATA 中启动真正的应用程序?

这很混乱,不是吗?我怎样才能更改自动更新程序,以便 UAC 不会踢我?

作为一个例子,我看到了firefox.exe。它如何更新,因此,当通常没有人可以在不提示用户的情况下写入该目录时直接写入C:\Program Files (x86)\...。

克里斯

【问题讨论】:

  • 软件初次安装时,需要安装一个服务来负责自动更新。不要将服务配置为自动启动,而是将其配置为无需管理员权限即可启动。确保服务在安装之前正确验证更新是真实的(例如,通过验证它是否使用您的证书签名)。
  • 您的意思是服务FooUpdater.exe 将在没有管理员权限的情况下执行?但是该服务如何修改 ProgramFiles 目录中的 Foo.exe 之类的文件?我对代码签名不太了解,所以我必须为此签名吗?请给我更详细的信息……
  • 不,我的意思是以管理员权限运行的服务。

标签: c windows nsis uac


【解决方案1】:

Firefox 使用 NT 服务。服务通常以 SYSTEM 身份运行,因此它可以将文件复制到任何地方,而 Mozilla 会配置其服务,以便普通用户可以启动。

在程序文件中有一个启动器,在 appdata 中有一个主应用程序是没有意义的,在这种情况下,您最好按用户安装(如 Chrome 等)。

我假设您的应用程序不是网络浏览器,因此保持其最新可能不是那么重要。我建议您的更新程序只检查更新,如果有更新,您可以使用 UAC 以管理员身份再次生成自己(或主更新程序?)然后进行更新。

【讨论】:

  • 但是我的程序在没有 gui 的情况下在后台运行。所以用户什么都看不到,也不应该对更新 UAC 确认弹出窗口感到恼火。让我的程序保持最新很重要,我目前不知道多久会发布一次更新。我的愿景是用户安装程序并且看不到它正在运行。他将在门户网站中看到结果。
  • 这些基本上是您的 3 个选项:1) 在 $localappdata\Programs 中为每个用户安装。 2) 当有新的更新可用时,UAC 会提示。 3) 编写服务和/或计划任务。
  • 所以我现在已经通过CreateService() 安装了该服务(需要管理员权限)。但是,当我尝试从一个没有管理员权限的程序(比如我的Foo.exe)StartService() 时,我收到一个 AccessDenied 错误。我使用SERVICE_START 打开服务句柄并使用SERVICE_DEMAND_START 创建服务。但是你说我可以作为普通用户启动服务。
  • 我说“Mozilla 配置了他们的服务,所以它可以由普通用户启动”,这意味着您必须在创建服务 ACL 后更改它... (SetServiceObjectSecurity)
【解决方案2】:

您的“更新” 进程需要以管理权限运行。

如果启用了 UAC,那么您可以通过将 runas 动词传递给 ShellExecute 来要求 Windows 启动升级的更新程序:

Boolean RunAsAdmin(HWND hWnd, String filename, String, parameters)
{
    /*
       See Step 3: Redesign for UAC Compatibility (UAC)
       http://msdn.microsoft.com/en-us/library/bb756922.aspx
    */
    ShellExecuteInfo sei;

    ZeroMemory(ref sei, sizeoOf(sei));
    sei.cbSize = sizeof(TShellExecuteInfo);
    sei.Wnd = hwnd;
    sei.fMask = SEE_MASK_FLAG_DDEWAIT || SEE_MASK_FLAG_NO_UI;
    sei.lpVerb = "runas";
    sei.lpFile = Filename;
    if (parameters <> '')
        sei.lpParameters = parameters;
    sei.nShow = SW_SHOWNORMAL;

    return ShellExecuteEx(ref sei);
}

如果未启用 UAC(例如,用户在 Windows XP 上运行),用户将别无选择,只能以具有管理权限的用户身份注销并登录。

注意:任何发布到公共领域的代码。无需署名。

【讨论】:

    猜你喜欢
    • 2013-02-16
    • 1970-01-01
    • 1970-01-01
    • 2016-01-28
    • 2013-06-11
    • 2010-12-13
    • 2013-09-10
    • 2020-08-29
    • 2011-04-27
    相关资源
    最近更新 更多