[英]Registration-Free COM Interop: Deactivating activation context in finalizer throws SEHException
我目前正在混合管理/本機工作鏈上工作,需要為免注冊COM支持創建激活上下文(請參閱在本機/托管環境中將免注冊COM清單嵌入C#dll中 )。 下面的代碼段是C#DLL內的一個更大的類的一部分,該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;
}
}
問題出在DestroyActivationContext()
方法內部的Pinactivated DeactivateActCtx()
函數。 調用后, SEHException
引發SEHException
:外部組件引發了異常。 0x80004005的。
沒有通過Marshal.GetLastWin32Error()
函數提供的錯誤代碼,它將為我提供一些合理的信息。
到目前為止我嘗試過的事情:
DestroyActivationContext()
函數從析構函數移至Dispose
方法,反之亦然。 IDisposable
接口。 DeactivateActCtx()
函數提供DEACTIVATE_ACTCTX_FLAG_FORCE_EARLY_DEACTIVATION
作為輸入參數。 IntPtr
實例的類型更改為UIntPtr
。 不幸的是,這些選擇都沒有幫助。 有沒有可能在不遇到上述SEHException
情況下取消激活上下文的SEHException
?
UPDATE
看來垃圾收集器的線程是問題的原因。 GC始終在其自己的獨立線程中運行,沒有明顯的可能性可以指定其他方式。 嘗試從此特定線程DeactivateActCtx
激活上下文( DeactivateActCtx
)時,似乎在后台發生了某種訪問沖突。 因此,我想除了在每個包裝的調用中激活和停用激活上下文之外,沒有簡單的方法來處理這種麻煩。 仍然歡迎提出任何其他建議。
為了使它起作用,每個包裝的調用都需要包含一個激活和隨后的取消激活請求。 感謝David Heffernan ,他提供了解決此問題的合理方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.