.NET Core 3.1中使用WindowsInstaller COM修改MSI报错排查
问题背景
我正在开发一款通过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

