You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core 3.1中使用WindowsInstaller COM修改MSI报错排查

MSI多实例部署:.NET Core修改UpgradeCode报错问题解决

问题背景

我正在开发一款通过WiX制作的Windows Installer安装包(.msi)部署的小型应用。随着需求变更,同一程序需在同一电脑上多实例安装并配置不同参数。为此我编写了一个命令行工具来修改现有.msi,该工具需完成三个操作:

  • 修改Property表中的ProductName;
  • 修改Upgrade表中ActionProperty为NEWERFOUND、SELFFOUND、PREVIOUSFOUND对应的UpgradeCode(此步骤失败);
  • 重新签名msi。

由于缺少.NET Core相关文档,我参考了.NET Framework的示例编写修改UpgradeCode的代码,但调用view.Modify时始终抛出PlatformNotSupportedException异常,而修改ProductName的功能正常。

相关代码

public void SetUpgradeCodes(string msiPath, string upgradeCode)
{
    var type = Type.GetTypeFromProgID("WindowsInstaller.Installer");
    
    var installer = (WindowsInstaller.Installer)Activator.CreateInstance(installerType);
    
    var db = installer.OpenDatabase(msiPath, MsiOpenDatabaseMode.msiOpenDatabaseModeDirect);

    view = db.OpenView("SELECT * FROM Upgrade WHERE ActionProperty = 'NEWERFOUND' OR ActionProperty = 'SELFFOUND' OR ActionProperty = 'PREVIOUSFOUND'");
    view.Execute();
    
    for(var i = 0; i<3; i++) {
        var record = view.Fetch();
        record.StringData[1] = upgradeCode;
        view.Modify(MsiViewModify.msiViewModifyReplace, record);
    }

    db.Commit();
    view.Close();

    Marshal.FinalReleaseComObject(view);
    Marshal.FinalReleaseComObject(db);
}

报错信息

System.PlatformNotSupportedException: Operation is not supported on this platform.
   at System.Runtime.InteropServices.Marshal.GetIDispatchForObject(Object o)
   at System.RuntimeType.WrapArgsForInvokeCall(Object[] aArgs, Int32[] aArgsWrapperTypes)
   at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Object[] aArgs, Boolean[] aArgsIsByRef, Int32[] aArgsWrapperTypes, Type[] aArgsTypes, Type retType)
   at WindowsInstaller.View.Modify(MsiViewModify Mode, Record Record)

.csproj配置

<COMReference Include="WindowsInstaller">
  <Guid>{000C1092-0000-0000-C000-000000000046}</Guid>
  <VersionMajor>1</VersionMajor>
  <VersionMinor>0</VersionMinor>
  <Lcid>1033</Lcid>
  <WrapperTool>tlbimp</WrapperTool>
  <Isolated>False</Isolated>
</COMReference>

请问该COM引用在.NET Core中是否无法完全使用?我哪里操作有误或遗漏了什么配置?


分析与解决方案

1. COM引用在.NET Core中的局限性

Windows Installer的COM组件(WindowsInstaller.Installer)属于旧式IDispatch组件,.NET Core对这类组件的复杂交互支持不完善。当调用view.Modify传递Record对象时,.NET Core无法正确处理IDispatch类型的参数传递,从而触发PlatformNotSupportedException。而修改ProductName功能正常,是因为对应的操作无需传递复杂COM对象,仅需简单的属性赋值或基础方法调用。

2. 最优解决方案:使用WiX Dtf库

WiX提供的**Deployment Tools Foundation (Dtf)**是专门用于MSI文件操作的.NET库,完全兼容.NET Core/.NET 5+,无需依赖COM交互,避免了平台支持问题。

步骤1:安装NuGet包

在项目中安装WixToolset.Dtf.WindowsInstaller包。

步骤2:重写修改UpgradeCode的代码

using WixToolset.Dtf.WindowsInstaller;

public void SetUpgradeCodes(string msiPath, string upgradeCode)
{
    // 使用using自动释放资源,无需手动调用Marshal.FinalReleaseComObject
    using var db = new Database(msiPath, DatabaseOpenMode.Direct);
    
    using var view = db.OpenView("SELECT * FROM Upgrade WHERE ActionProperty IN ('NEWERFOUND', 'SELFFOUND', 'PREVIOUSFOUND')");
    view.Execute();
    
    Record record;
    // 循环获取所有匹配记录,避免硬编码循环次数
    while ((record = view.Fetch()) != null)
    {
        // 通过字段名赋值,比索引更直观且不易出错
        record["UpgradeCode"] = upgradeCode;
        view.Modify(ViewModifyMode.Replace, record);
    }
    
    db.Commit();
}

3. 项目配置修正

确保项目目标平台为Windows,在.csproj中添加或修改以下配置:

<PropertyGroup>
  <!-- 根据实际使用的.NET版本调整,如net7.0-windows -->
  <TargetFramework>net6.0-windows</TargetFramework>
  <!-- 确保启用Windows平台相关API支持 -->
  <UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>

4. 原代码的额外问题修正

原代码中存在变量名错误:Activator.CreateInstance(installerType)应为Activator.CreateInstance(type),虽然这不是报错的直接原因,但建议修正以避免潜在问题。


内容的提问来源于stack exchange,提问作者Damo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 10:06:46