我有一个自定义安装应用程序,可以安装多个MSI文件。 今天我尝试使用this文章实现外部UI来实现我自己的进度条。一切看起来都有效(进度条接收数据和更新)但在开始更新组件后大约60%时我收到一个异常:'对象未设置为...'并进一步挖掘得到:_COMPlusExceptionCode“-532462766”
检查了进程监视器,突然意识到msiexec正在以32位模式运行。
有趣的是直接调用msiexe时它以64位开始,但是使用MsiInstallProduct()方法它以32位开始。
我相信当msiexec尝试配置注册表项时会引发异常,并且当MSI文件为64位时会崩溃。
任何帮助表示感谢。
干杯, 阿夫欣
更新1: 使用MsiEnableLog启用日志并显示此错误:
“MSI(c)(94:F8)[07:50:29:395]:安装操作期间的内部异常:0xc0000005位于0x000007FE9827F768。”
更新2: 根据@marceln的建议进一步挖掘,使用Process Monitor并注意到内存中有两个msiexec进程。一个在64位模式下,在会话0中,另一个在第一个调用MsiInstallProduct时启动。 第二个从32位版本的'c:\ windows \ syswow64 \ msiexec.exe'开始。 我尝试使用SetDllDirectory设置查找路径,但仍然得到相同的结果。
更新3: 主进程肯定是以64位模式运行: 两个prccess监视器都证明了这一点以及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接口并试图在“using”块中使用该类来确保完全处理该类。现在可以在此块中安全地调用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:感谢Marcel和Jacob:)
答案 0 :(得分:0)
我认为在运行两年没有问题的代码之后,最好在上面的Update 7中提出的工作流程是对这个问题的回答:
看起来MSI API中存在某种内部内存管理泄漏,导致外部UI处理程序以完全随机的行为崩溃。为了解决这个问题,我实现了IDisposable接口并试图在“using”块中使用该类来确保完全处理该类。现在可以在此块中安全地调用MsiSetExternalUI()和MsiInstallProduct()。不要忘记调用MsiSetExternalUI()将UI恢复为原始状态
答案 1 :(得分:0)
原因是垃圾收集器。请尝试使用GC.KeepAlive()方法来防止垃圾收集器收集外部UI处理程序。
例如:
// create ui handler
MSIIntrop.UIHandlerDelegate externalUIHandler = new MSIIntrop.UIHandlerDelegate(context.Handler);
// execute MsiSetExternalUI with this handler
MSIIntrop.INSTALLUI_HANDLER prevHandlre = MSIIntrop.MsiSetExternalUI(externalUIHandler,
MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_FATALEXIT |
MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_ERROR |
MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_WARNING |
MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_ACTIONDATA |
MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_PROGRESS,
IntPtr.Zero);
// install product
int ret = MSIIntrop.MsiInstallProduct(runningPath, commandLine);
// restore the previous ui handler
MSIIntrop.MsiSetExternalUI(prevHandlre, 0, IntPtr.Zero);
// prevent GC from collecting ExternalUIHandler during the product installation
GC.KeepAlive(externalUIHandler);