MsiInstallProduct()以32位模式启动msiexec.exe?

时间:2013-05-30 07:26:32

标签: c# msiexec

我有一个自定义安装应用程序,可以安装多个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:)

2 个答案:

答案 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);