現在、マネージド/ネイティブ ワーク チェーンが混在する作業を行っており、登録不要の COM サポート用のアクティベーション コンテキストを作成する必要があります (ネイティブ/マネージド環境で C# dll に登録不要の COM マニフェストを埋め込む を参照)。次のスニペットは、C# DLL 内のより大きなクラスの一部であり、COM ラッパーへの参照を保持し、必要なアクティベーション コンテキストを確立します。
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace FirstClient
{
public class FirstClientDLL : IDisposable
{
~FirstClientDLL()
{
Dispose(false);
}
void IDisposable.Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
DestroyActivationContext();
}
private bool DestroyActivationContext()
{
if (m_cookie != IntPtr.Zero)
{
try
{
//When being invoked from the destructor or the dispose method, the following line always fails...
if (!DeactivateActCtx(0, m_cookie))
return false;
m_cookie = IntPtr.Zero;
}
catch (SEHException ex)
{
// Always gets hit. Why??
Debug.Print(ex.Message + " " + "0x" + ex.ErrorCode.ToString("X"));
return false;
}
if (!ReleaseActCtx(m_hActCtx))
return false;
m_hActCtx = IntPtr.Zero;
}
return true;
}
public bool EstablishActivationContext()
{
ACTCTX info = new ACTCTX();
info.cbSize = Marshal.SizeOf(typeof(ACTCTX));
info.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
info.lpSource = System.Reflection.Assembly.GetExecutingAssembly().Location;
info.lpResourceName = ISOLATIONAWARE_MANIFEST_RESOURCE_ID;
m_hActCtx = CreateActCtx(ref info);
if (m_hActCtx == new IntPtr(-1))
return false;
m_cookie = IntPtr.Zero;
if (!ActivateActCtx(m_hActCtx, out m_cookie))
return false;
m_iCOMInstance = new atlw.TestClass();
// --> If I destroy the activation context here, no exception is thrown. Obviously, the COM wrapper will get invalidated and can no longer accept any calls.
//DestroyActivationContext();
return true;
}
public string CallCOMMethod()
{
return m_iCOMInstance.SayHello();
}
private const uint ACTCTX_FLAG_RESOURCE_NAME_VALID = 0x008;
private const UInt16 ISOLATIONAWARE_MANIFEST_RESOURCE_ID = 2;
private const UInt16 DEACTIVATE_ACTCTX_FLAG_FORCE_EARLY_DEACTIVATION = 1;
[DllImport("Kernel32.dll")]
private extern static IntPtr CreateActCtx(ref ACTCTX actctx);
[DllImport("Kernel32.dll")]
private extern static bool ActivateActCtx(IntPtr hActCtx, out IntPtr lpCookie);
[DllImport("Kernel32.dll")]
private extern static bool DeactivateActCtx(uint dwFlags, IntPtr lpCookie);
[DllImport("Kernel32.dll")]
private extern static bool ReleaseActCtx(IntPtr hActCtx);
private struct ACTCTX
{
public int cbSize;
public uint dwFlags;
public string lpSource;
public ushort wProcessorArchitecture;
public ushort wLangId;
public string lpAssemblyDirectory;
public UInt16 lpResourceName;
public string lpApplicationName;
public IntPtr hModule;
}
private atlw.ITestClass m_iCOMInstance;
private IntPtr m_cookie;
private IntPtr m_hActCtx;
}
}
問題は、メソッド内の pinvokedDeactivateActCtx()
関数にありDestroyActivationContext()
ます。呼び出されるとすぐにSEHException
、例外がスローされます: 外部コンポーネントが例外をスローしました。0x80004005。
Marshal.GetLastWin32Error()
関数を介して利用できるエラー コードはありません。これにより、妥当な情報が得られます。
私がこれまでに試したこと:
DestroyActivationContext()
関数をデストラクタからDispose
メソッドに、またはその逆に移動します。IDisposable
インターフェイスを完全に削除します。- 基になる COM オブジェクトのスレッド モデルを Apartment から Free に変更します。
- 入力引数として
DeactivateActCtx()
関数を提供します。DEACTIVATE_ACTCTX_FLAG_FORCE_EARLY_DEACTIVATION
IntPtr
インスタンスのタイプを に変更しますUIntPtr
。
残念ながら、これらのオプションはどれも役に立ちませんでした。前述のことに直面せずにアクティベーションコンテキストを無効にする方法はありますSEHException
か?
アップデート
ガベージコレクタのスレッドが問題の原因のようです。GC は常に独自のスレッドで実行され、他に指定する可能性は明らかではありません。DeactivateActCtx
この特定のスレッドからアクティベーション コンテキスト ( ) を非アクティブ化しようとすると、内部で何らかのアクセス違反が発生しているようです。したがって、ラップされた各呼び出しでアクティブ化コンテキストをアクティブ化および非アクティブ化する以外に、この厄介さに対処する簡単な方法はないと思います。そうでないことを証明する提案は、まだ大歓迎です。