4

複数の MSI ファイルをインストールするカスタマイズされたセットアップ アプリケーションがあります。今日、この記事を使用して、独自の進行状況バーを実装するために外部 UI を実装しようとしました。すべてが機能しているように見えます (プログレスバーはデータと更新を受け取ります) が、コンポーネントの更新を開始するときに約 60% 後に、「オブジェクトが設定されていません ...」という例外を受け取り、さらに掘り下げると、次のようになりました: _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 を使用し、メモリ内に 2 つの msiexec プロセスがあることに気付きました。1 つはセッション 0 にある 64 ビット モードで、もう 1 つは MsiInstallProduct を呼び出すと最初のモードで開始されます。2 つ目は、32 ビット バージョンの「c:\windows\syswow64\msiexec.exe」から始まります。SetDllDirectoryを使用してルックアップ パスを設定しようとしましたが、それでも同じ結果が得られました。

更新 3: メイン プロセスは間違いなく 64 ビット モードで実行されています: 両方のプロセス モニターがこれを証明し、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: マルセルとジェイコブに感謝 :)

4

2 に答える 2

0

2 年間コードを問題なく実行した後、上記の Update 7 で提案されたワークフローが次の質問に対する答えであると結論付けるのが適切であると思います。

MSI API にある種の内部メモリ管理リークがあり、完全にランダムな動作で外部 UI ハンドラがクラッシュするようです。この問題を解決するために、IDisposable インターフェイスを実装し、「using」ブロックでクラスを使用して、クラスが完全に破棄されるようにしました。MsiSetExternalUI() と MsiInstallProduct() は、このブロック内で安全に呼び出されるようになりました。MsiSetExternalUI() を呼び出して、UI を元の状態に戻すことを忘れないでください。

于 2015-12-01T22:48:13.260 に答える