WiX:在启用 UAC 的情况下建立符号链接

发布于 2024-09-16 09:32:46 字数 1558 浏览 4 评论 0原文

我想在 Windows Installer(使用 WiX 脚本)中执行自定义操作,在安装结束时创建符号链接。 mklink 需要管理员权限,因为安装程序有限制。这就是我写的:

<CustomAction Id="mklink_cmdline" Property="QtExecCmdLine" Value='"[SystemFolder]cmd.exe" /c mklink "[SystemFolder]my_app.dll" "[INSTALLDIR]my_app.dll"' />
<CustomAction Id="mklink_exec" BinaryKey="WixCA" DllEntry="CAQuietExec" Return="ignore" />

...

<InstallExecuteSequence>
    <Custom Action="mklink_cmdline" Before="InstallFinalize">
        ...
    </Custom>
    <Custom Action="mklink_exec" After="mklink_cmdline">
        ...
    </Custom>
    ...
</InstallExecuteSequence>

如果 UAC 完全禁用,这将非常有效。但是,在任何级别启用 UAC 时,

CAQuietExec:  You do not have sufficient privilege to perform this operation.

即使我在同意窗口中允许,此自定义操作也会失败。我尝试将“执行”更改为“延迟”,将“模拟”更改为“否”,或将包的“InstallPrivileges”更改为“提升”,但这些都不起作用。

有什么建议我可以绕过吗?谢谢你!

编辑:使用延迟的自定义操作修改代码

<CustomAction Id="mklink_cmdline" Property="mklink_exec" Value='"[SystemFolder]cmd.exe" /c mklink "[SystemFolder]my_app.dll" "[INSTALLDIR]my_app.dll"' />
<CustomAction Id="mklink_exec" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Impersonate="no" Return="ignore" />

...

<InstallExecuteSequence>
    <Custom Action="mklink_exec" Before="InstallFinalize">
        ...
    </Custom>
    <Custom Action="mklink_cmdline" Before="mklink_exec">
        ...
    </Custom>
    ...
</InstallExecuteSequence>

I want to execute a custom action in a Windows Installer (with WiX script) that makes symbolic links at the end of installation. mklink requires administrator privilege, as the installer restricts. This is what I wrote:

<CustomAction Id="mklink_cmdline" Property="QtExecCmdLine" Value='"[SystemFolder]cmd.exe" /c mklink "[SystemFolder]my_app.dll" "[INSTALLDIR]my_app.dll"' />
<CustomAction Id="mklink_exec" BinaryKey="WixCA" DllEntry="CAQuietExec" Return="ignore" />

...

<InstallExecuteSequence>
    <Custom Action="mklink_cmdline" Before="InstallFinalize">
        ...
    </Custom>
    <Custom Action="mklink_exec" After="mklink_cmdline">
        ...
    </Custom>
    ...
</InstallExecuteSequence>

This works perfectly if UAC is completely disabled. However, when enabling UAC in any level, this custom action fails with

CAQuietExec:  You do not have sufficient privilege to perform this operation.

even if I allowed in the consent window. I tried to change Execute to deferred, Impersonate to no, or change package's InstallPrivileges to elevated, none of them works.

Any suggestion I can bypass? Thank you!

Edit: revised code with deferred custom action

<CustomAction Id="mklink_cmdline" Property="mklink_exec" Value='"[SystemFolder]cmd.exe" /c mklink "[SystemFolder]my_app.dll" "[INSTALLDIR]my_app.dll"' />
<CustomAction Id="mklink_exec" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Impersonate="no" Return="ignore" />

...

<InstallExecuteSequence>
    <Custom Action="mklink_exec" Before="InstallFinalize">
        ...
    </Custom>
    <Custom Action="mklink_cmdline" Before="mklink_exec">
        ...
    </Custom>
    ...
</InstallExecuteSequence>

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

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

发布评论

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

评论(4

琴流音 2024-09-23 09:32:46

从管理员命令提示符运行时它是否有效?我想确实如此。

据我发现,msi 无法提高 UAC 级别,而这正是您所需要的。我必须创建一个 setup.exe,将 msi 包装为嵌入式资源并执行它。 setup.exe 包含请求管理员执行级别的 app.manifest,该级别会适当提高 UAC 级别:

<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <assemblyIdentity version="1.0.0.0" name="Setup.app"/>
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
        <security>
            <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
                <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
            </requestedPrivileges>
        </security>
    </trustInfo>
</asmv1:assembly>

我可能对 WIX、自定义操作和 UAC 不够了解,但这就是我最终所做的。

Does it work when ran from an administrator command prompt? I assume it does.

From what I found the msi cannot raise the UAC level which is what you need here. I had to create a setup.exe that wrapped the msi as an embedded resource and executed it. The setup.exe includes the app.manifest requesting administrator execution level which raises the UAC level appropriately:

<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <assemblyIdentity version="1.0.0.0" name="Setup.app"/>
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
        <security>
            <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
                <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
            </requestedPrivileges>
        </security>
    </trustInfo>
</asmv1:assembly>

I may just not understand WIX, custom actions and UAC enough, but this is what I ended up doing.

温馨耳语 2024-09-23 09:32:46

当您将其标记为“延迟”时,是否将其安排在 InstallInitialize 和 InstallFinalize 之间?您的之前和之后看起来有点奇怪:

InstallFinalize

_cmdline before InstallFinalize

_mkline_exec after _cmdline

听起来有点不确定。您可能会发现 _cmdline 在 InstallFinalize 之后发生,并且 deferred 在那里不起作用。

尝试:

InstallFinalize

_exec 之前 InstallFinalize

_cmldline 之前 _exec

Are you scheduling it between InstallInitialize and InstallFinalize when you mark it for Deferred? Your Before and after looks a little wierd:

InstallFinalize

_cmdline before InstallFinalize

_mkline_exec after _cmdline

Sounds a little nondeterministic. You might find _cmdline occurring after InstallFinalize and deferred won't work there.

Try:

InstallFinalize

_exec before InstallFinalize

_cmldline before _exec

不再见 2024-09-23 09:32:46

如果实际上是 mklink 需要提升,您可以尝试使用 SysInternals junction.exe。

If it's actually mklink that is requiring elevation, you might try using SysInternals junction.exe instead.

刘备忘录 2024-09-23 09:32:46

我最终将 wintellect 中的 elevate.exe 捆绑在一起,将其部署到某个临时文件夹,并为其提供创建所有符号链接的命令行脚本的路径。比它是通过自定义操作调用的。
命令行文件又具有一些优点来检测正确的程序文件文件夹。或者如果需要的话从命令行获取它。
看来,即使 WiX 正确地提升了自定义操作,msi(或 Windows Installer)本身也没有授予它足够的权限来正确运行 mklink 命令。

另请注意 CA 中的Impersonate="yes"。我相信这将使 msi 在执行操作时显示海拔对话框。

命令行文件:

cd /D %~p0

IF EXIST "%PROGRAMFILES(x86)%" SET PROGFILES=%PROGRAMFILES(x86)%
IF "%PROGFILES%".=="". SET PROGFILES=%PROGRAMFILES%

SET INSTALLPATH=%PROGFILES%\MyGreatProduct
SET DATAPATH=%PROGRAMDATA%\MyGreatProduct

IF NOT "%~1."=="." SET INSTALLPATH=%~1
IF NOT "%~2."=="." SET DATAPATH=%~2

IF EXIST "%INSTALLPATH%" mklink "%INSTALLPATH%\veryimportant.ini" "%DATAPATH%\veryimportant.ini"

在wxs文件中:

<Component Directory="TempFolder" Id='Comp_Temp_Makesymlinks' Guid='47a58219-1291-4321-4321-176987154921'>
    <File Id='makesymlinks_cmd' Source='makesymlinks.cmd'>
                <Permission User='Everyone' GenericAll='yes' />
    </File>
    <File Id='elevate_exe' Source='elevate.exe'>
                <Permission User='Everyone' GenericAll='yes' />
    </File>
</Component>

<SetProperty Id="CA_MakeSymLinksCmd" Before="CA_MakeSymLinksCmd" Sequence="execute" 
    Value=""[TempFolder]\elevate.exe" "[TempFolder]\makesymlinks.cmd"" />
<CustomAction Id="CA_MakeSymLinksCmd" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Return="ignore" Impersonate="yes" />

<InstallExecuteSequence>
    <Custom Action="CA_MakeSymLinksCmd" Before="InstallFinalize"><![CDATA[NOT Installed AND VersionNT >= 600 ]]></Custom>
</InstallExecuteSequence>

I ended up bundling elevate.exe from wintellect, deploy it to some temp folder and supply it with a path to command-line script which created all symbolic links. Than it was invoked via the custom action.
Command line file in turn has some goodness inside to detect proper program files folder. or get it from the command line, if needed.
It appears that even though WiX correctly elevates the custom action, msi (or Windows Installer) itself doesn't grant it sufficient rights to properly run mklink command.

Also note that Impersonate="yes" in the CA. I believe that's what will let msi to show elevation dialog box when it executes the action.

command line file:

cd /D %~p0

IF EXIST "%PROGRAMFILES(x86)%" SET PROGFILES=%PROGRAMFILES(x86)%
IF "%PROGFILES%".=="". SET PROGFILES=%PROGRAMFILES%

SET INSTALLPATH=%PROGFILES%\MyGreatProduct
SET DATAPATH=%PROGRAMDATA%\MyGreatProduct

IF NOT "%~1."=="." SET INSTALLPATH=%~1
IF NOT "%~2."=="." SET DATAPATH=%~2

IF EXIST "%INSTALLPATH%" mklink "%INSTALLPATH%\veryimportant.ini" "%DATAPATH%\veryimportant.ini"

in the wxs file:

<Component Directory="TempFolder" Id='Comp_Temp_Makesymlinks' Guid='47a58219-1291-4321-4321-176987154921'>
    <File Id='makesymlinks_cmd' Source='makesymlinks.cmd'>
                <Permission User='Everyone' GenericAll='yes' />
    </File>
    <File Id='elevate_exe' Source='elevate.exe'>
                <Permission User='Everyone' GenericAll='yes' />
    </File>
</Component>

<SetProperty Id="CA_MakeSymLinksCmd" Before="CA_MakeSymLinksCmd" Sequence="execute" 
    Value=""[TempFolder]\elevate.exe" "[TempFolder]\makesymlinks.cmd"" />
<CustomAction Id="CA_MakeSymLinksCmd" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Return="ignore" Impersonate="yes" />

<InstallExecuteSequence>
    <Custom Action="CA_MakeSymLinksCmd" Before="InstallFinalize"><![CDATA[NOT Installed AND VersionNT >= 600 ]]></Custom>
</InstallExecuteSequence>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文