複数の 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: マルセルとジェイコブに感謝 :)