旧文件夹的msisettargetPatha在升级时以1315失败(无法写入指定文件夹)
序言
我们已更改了产品的默认安装文件夹,但是我们现有客户端需要在升级上保留旧的安装路径。 更新:独立msi
和msm
模块是用Wix构建的。但是,整个项目是使用Wix MSM
S模块构建的。因此,应在两种项目类型(WIX和InstallShield)中重复使用自定义。
选择的解决方案
由于installdir
在目录
表中的输入已更改,因此我需要在自定义期间找到已安装产品的路径并修改installdir
行动。
这是sopitoldPathonUpgrade
自定义代码的一部分,该代码是在Costfinalize
之后立即执行的:
guid_str_t guidBuffer{};
auto bufferLenght = DWORD(guidBuffer.size());
constexpr const auto propUpgradingProduct = "UPGRADING_PRODUCT";
UINT res = MsiGetPropertyA(hInstall,
propUpgradingProduct,
guidBuffer.data(),
&bufferLenght);
if (ERROR_SUCCESS != res)
throw std::runtime_error(std::string("Failed to get property \"") +
propUpgradingProduct +
"\", Error: " +
getLastError());
logMessage(std::string() + __func__ + ": current INSTALLDIR = \"" + getCurrentInstallDir() + "\"");
const auto& oldPath = getPathByGUID(guidBuffer);
logMessage(std::string() + __func__ + ": setting INSTALLDIR to old path: \"" + oldPath + "\"");
constexpr const auto installFolderIdentifier = "INSTALLDIR";
res = MsiSetTargetPathA(hInstall, installFolderIdentifier, oldPath.c_str());
if (ERROR_SUCCESS != res)
throw std::runtime_error(std::string("Failed to set INSTALLDIR on an product update. Error: ") +
getLastError());
这是Wix定义:
<Fragment>
<CustomAction Id="AssignOldPathOnUpgrade"
BinaryKey="DLServiceCA"
DllEntry="AssignOldPathOnUpgrade"
Execute="immediate"
Impersonate="no"
Return="check" />
<Binary Id="DLServiceCA" SourceFile="$(var.DLServiceCA.TargetPath)" />
</Fragment>
和Wix installexecutesequence
:
<Custom Action="AssignOldPathOnUpgrade" After="CostFinalize">
UPGRADING_PRODUCT
</Custom>
问题:问题
。 CUSTIOMACTION因msisettargetPatha
返回error_directory
带有安装程序的内部错误1315
代表无法写入指定文件夹 。
- 如果升级安装是由
管理员
组成员的用户运行的:启动UAC后不久要求高程。1315
当调用soprionoldPathonupgrade
时出现错误。 - 如果升级安装是使用管理特权(作为管理员)运行的,则升级是成功的,并且将新版本复制到旧路径。
- 以前的升级具有相同的(硬编码)安装目录,因为旧升级目录是由UAC高程(第一个方案)启动的旧升级。
dirctory组件
的所有先前版本的WIX项目都通过installDir
通过组件
andiss pripiliges:
<Fragment>
<?if $(sys.BUILDARCH) = x86?>
<?define INSTALLDIRComponent.Id = "{155AB3A7-0160-419D-AC7D-7BA88AF6E938}" ?>
<?elseif $(sys.BUILDARCH) = x64?>
<?define INSTALLDIRComponent.Id = "{E2186CCC-A834-4F37-B58C-F55A11E9E5DD}" ?>
<?endif?>
<Component Directory="INSTALLDIR"
Id="INSTALLDIRComponent"
Guid="$(var.INSTALLDIRComponent.Id)"
Location="local"
Win64="$(var.Win64)" >
<CreateFolder>
<Permission User="Administrators" GenericAll="yes"/>
<Permission User="Everyone" Read="yes" GenericRead="yes"/>
</CreateFolder>
</Component>
</Fragment>
结论
- 我如何解决此错误解决问题(没有某些疯狂的休息性解决方法)?用户必须能够像使用Previos版本一样进行UAC高程的升级。
- 当
installDir
是硬编码时,为什么可以将(使用UAC高程)升级到旧路径,而1315
当调用msisettargetPatha
时出现错误?
更新
MsisettArgetPath如果仅读取选定目录,则会失败。
当我启用读取
用户
组的权限时,msisettargetPatha
成功。但是,它从来没有启用用户
,因此在调用mSisettArgetPatha
的电话之间切换此prsight 是不是一个选项。
更新2
允许UAC进行安装后,我发现的是,当前用户的MSI过程是 升高的:
因此,我认为它不代表管理员行动(但是, hummy 是管理员组的成员组的成员),因此许可 Write 不适用于虚拟在未提升的上下文中。
当路径被硬编码时,为什么它成功将文件复制到目录? - 我想这是因为复制是由系统执行的。这是一个猜测。
Preface
We have changed the default installation folder for a product, but there is a requirement from our existing clients to preserve the old installation path on an upgrade. Update: standalone msi
and msm
modules are built with Wix. However, the whole project is built with InstallShield using Wix msm
s modules. So the CustomAction should be reusable in both project types (Wix and InstallShield).
Chosen solution
Since the INSTALLDIR
entry in a Directory
table has changed, I need to find the path of the installed product and modify INSTALLDIR
during a custom action.
This is a part of AssignOldPathOnUpgrade
CustomAction's code, which is executed right after CostFinalize
:
guid_str_t guidBuffer{};
auto bufferLenght = DWORD(guidBuffer.size());
constexpr const auto propUpgradingProduct = "UPGRADING_PRODUCT";
UINT res = MsiGetPropertyA(hInstall,
propUpgradingProduct,
guidBuffer.data(),
&bufferLenght);
if (ERROR_SUCCESS != res)
throw std::runtime_error(std::string("Failed to get property \"") +
propUpgradingProduct +
"\", Error: " +
getLastError());
logMessage(std::string() + __func__ + ": current INSTALLDIR = \"" + getCurrentInstallDir() + "\"");
const auto& oldPath = getPathByGUID(guidBuffer);
logMessage(std::string() + __func__ + ": setting INSTALLDIR to old path: \"" + oldPath + "\"");
constexpr const auto installFolderIdentifier = "INSTALLDIR";
res = MsiSetTargetPathA(hInstall, installFolderIdentifier, oldPath.c_str());
if (ERROR_SUCCESS != res)
throw std::runtime_error(std::string("Failed to set INSTALLDIR on an product update. Error: ") +
getLastError());
Here is the Wix definition:
<Fragment>
<CustomAction Id="AssignOldPathOnUpgrade"
BinaryKey="DLServiceCA"
DllEntry="AssignOldPathOnUpgrade"
Execute="immediate"
Impersonate="no"
Return="check" />
<Binary Id="DLServiceCA" SourceFile="$(var.DLServiceCA.TargetPath)" />
</Fragment>
And Wix InstallExecuteSequence
:
<Custom Action="AssignOldPathOnUpgrade" After="CostFinalize">
UPGRADING_PRODUCT
</Custom>
The Problem
The CustiomAction fails due to MsiSetTargetPathA
returning ERROR_DIRECTORY
with Installer's internal error 1315
which stands for Unable to write to the specified folder.
- If the upgrade installation is run by a user who is a member of
Administrators
group: shortly after launch UAC asks for elevation. And1315
error occurres whenAssignOldPathOnUpgrade
is invoked. - If the upgrade installation is run with the administrative priviliges (Run as Administrator), the upgrade is successfull and the new version is copied to the old path.
- Previous upgrades that had the same (hardcoded) default directory for installation as the old one are successful when launched by a user with UAC elevation (the 1st scenario).
Dirctory component
The Wix project for all the previous versions creates the INSTALLDIR
via a Component
and assigns priviliges:
<Fragment>
<?if $(sys.BUILDARCH) = x86?>
<?define INSTALLDIRComponent.Id = "{155AB3A7-0160-419D-AC7D-7BA88AF6E938}" ?>
<?elseif $(sys.BUILDARCH) = x64?>
<?define INSTALLDIRComponent.Id = "{E2186CCC-A834-4F37-B58C-F55A11E9E5DD}" ?>
<?endif?>
<Component Directory="INSTALLDIR"
Id="INSTALLDIRComponent"
Guid="$(var.INSTALLDIRComponent.Id)"
Location="local"
Win64="$(var.Win64)" >
<CreateFolder>
<Permission User="Administrators" GenericAll="yes"/>
<Permission User="Everyone" Read="yes" GenericRead="yes"/>
</CreateFolder>
</Component>
</Fragment>
Conclusion
- How can I solve the problem with this error (without some insanelly convoluted workarounds)? User must be able to do an upgrade with UAC elevation just like with previos versions.
- Why is it possible to upgrade (with UAC elevation) to an old path when
INSTALLDIR
is hardcoded, but1315
error occurres whenMsiSetTargetPathA
is called?
Update
The documentation says:
MsiSetTargetPath fails if the selected directory is read only.
When I enable Read
permission for Users
group, MsiSetTargetPathA
succeeds. But it is not ever enabled for Users
, so toggling this prmission between calls to MsiSetTargetPathA
is not an option.
UPDATE 2
After allowing UAC to proceed with installation I found out, that the msi process from the current user is not elevated:
Hence, I think it does not act on behalf of an Administrator (however, dummy is a member of Administrators group), so the Permission to Write does not apply to dummy in an unelevated context.
Why does it successfully copy files to the directory when the path is hardcoded? - I guess it is because the copying is performed by the SYSTEM. This is a guess though.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您应该使用更改目录路径。如果您确实需要运行自定义代码以获取上一个路径,则应设置一个新属性,然后使用该新属性
setDirectory
应将目录设置为值。You should be using
SetDirectory
to change the directory path. If you really need to run custom code to get the previous path, then it should set a new property and then aSetDirectory
should set the directory to the value using that new property.