我有一个自定义的安装应用程序,它安装了几个 MSI 文件。今天我尝试使用这篇文章实现外部UI来实现我自己的进度条。一切看起来都正常(progressbar 接收数据和更新)但是在开始更新组件时大约 60% 后我收到一个异常:'object not set to ...' 并进一步挖掘得到这个:_COMPlusExceptionCode“-532462766”
检查进程监视器,突然意识到 msiexec 正在以 32 位模式运行。
有趣的是,直接调用 msiexe 时,它以 64 位开始,但使用 MsiInstallProduct() 方法,它以 32 位开始。
我相信当 msiexec 尝试配置注册表项时会引发异常,并且由于 MSI 文件是 64 位,它会崩溃。
任何帮助表示赞赏。
干杯,阿夫辛
更新 1: 使用 MsiEnableLog 启用日志并出现此错误:
“MSI (c) (94:F8) [07:50:29:395]:安装操作期间的内部异常:0x000007FE9827F768 处的 0xc0000005。”
更新 2: 根据@marceln 的建议进一步挖掘,使用 Process Monitor 并注意到内存中有两个 msiexec 进程。一个在 64 位模式下,在会话 0 中,另一个在我调用 MsiInstallProduct 时从第一个开始。第二个从 32 位版本的 'c:\windows\syswow64\msiexec.exe' 开始。我尝试使用SetDllDirectory设置查找路径,但仍然得到相同的结果。
更新 3: 主进程肯定在 64 位模式下运行:prccess monitor 和 powershell 命令结果都证明了这一点:
[reflection.assemblyname]::GetAssemblyName("setup.exe") | fl
Name : Setup
Version : 5.0.0.0
CultureInfo :
CultureName :
CodeBase : file:///...../Setup.exe
EscapedCodeBase : file:///Setup.exe
ProcessorArchitecture : **MSIL**
ContentType : Default
Flags : None
HashAlgorithm : SHA1
VersionCompatibility : SameMachine
KeyPair :
FullName : Setup, Version=5.0.0.0, Culture=neutral, PublicKeyToken=null
更新 4: 我正在使用此方法导入 MSI.DLL:
[DllImport("msi.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int MsiInstallProduct(string packagePath, string commandLine);
更新5: 尝试了进程资源管理器,应用程序是64位的,应用程序下的MSI.DLL文件是从system32运行的。但是 msiexec.exe 进程仍然从 32 位的 syswow64 运行。msi 文件构建为 64 位 msi。
更新6: 我刚刚发现这条线是问题的根源:
oldHandler = MSIIntrop.MsiSetExternalUI(
new MSIIntrop.InstallUIHandler(OnExternalUI),
32735,
IntPtr.Zero);
更新 7 [最终更新]: 它可能关心的人:经过数小时的浪费时间,我终于设法克服了这个问题。看起来 MSI API 中存在某种内部内存管理泄漏,导致外部 UI 处理程序以完全随机的行为崩溃。为了解决这个问题,我实现了 IDisposable 接口并尝试在“使用”块中使用该类以确保该类已完全释放。现在可以在此块中安全地调用 MsiSetExternalUI() 和 MsiInstallProduct()。不要忘记调用 MsiSetExternalUI() 将 UI 恢复为原始状态:
IntPtr prevWindow = IntPtr.Zero;
MSIIntrop.INSTALLUILEVEL prevUILevel = MSIIntrop.MsiSetInternalUI(MSIIntrop.INSTALLUILEVEL.INSTALLUILEVEL_NONE, ref prevWindow);
using (MSIContext context = new MSIContext(progressChanged, messageRaised))
{
MSIIntrop.INSTALLUI_HANDLER prevHandlre = MSIIntrop.MsiSetExternalUI(context.Handler,
MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_FATALEXIT |
MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_ERROR |
MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_WARNING |
MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_ACTIONDATA |
MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_PROGRESS,
IntPtr.Zero);
try
{
int ret = MSIIntrop.MsiInstallProduct(runningPath, commandLine);
}
catch (Exception ex)
{
messageRaised("Error: " + ex.Message);
}
finally
{
MSIIntrop.MsiSetExternalUI(prevHandlre, 0, IntPtr.Zero);
}
}
PS 1:我没有把这个放在答案中,因为我不确定这是错误的来源。这只是一种解决方法。PS 2:感谢马塞尔和雅各布 :)