4

我有一个自定义的安装应用程序,它安装了几个 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:感谢马塞尔和雅各布 :)

4

2 回答 2

0

我认为在代码运行两年后没有出现任何问题后,可以得出结论,上面更新 7 中提出的工作流程是对以下问题的回答:

看起来 MSI API 中存在某种内部内存管理泄漏,导致外部 UI 处理程序以完全随机的行为崩溃。为了解决这个问题,我实现了 IDisposable 接口并尝试在“使用”块中使用该类以确保该类已完全释放。现在可以在此块中安全地调用 MsiSetExternalUI() 和 MsiInstallProduct()。不要忘记调用 MsiSetExternalUI() 将 UI 恢复为原始状态

于 2015-12-01T22:48:13.260 回答
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);
于 2016-07-13T12:13:53.777 回答