현재 관리되는 네이티브 워크 체인이 혼합되어 있으므로 등록이 필요없는 COM 지원을위한 활성화 컨텍스트를 만들어야합니다 (Embed a Registration-Free COM manifest into a C# dll with native/managed environment 참조).등록이 필요없는 COM Interop : finalizer의 활성화 컨텍스트를 비활성화하면 SEHException이 발생합니다.
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()
있어서 내부 pinvoked DeactivateActCtx()
함수에 달려있다 : 다음 코드는 필요한 활성 컨텍스트의 COM 랩퍼에 대한 참조를 보유하고 설정하는 C# 1 DLL 내부의 큰 클래스의 일부이다. 전화가 걸리 자마자 SEHException
가 발생합니다. 외부 구성 요소에서 예외가 발생했습니다. 0x80004005.
Marshal.GetLastWin32Error()
함수를 통해 사용할 수있는 오류 코드가 없으므로 합리적인 정보를 얻을 수 있습니다.
것들 지금까지 시도 :
- 가
Dispose
방법 소멸자에서DestroyActivationContext()
기능을 이동 및 그 반대의 경우도 마찬가지입니다. IDisposable
인터페이스를 모두 제거하십시오.- 기본 COM 개체의 스레딩 모델을 아파트에서 무료로 변경합니다.
- 입력 인수로
DEACTIVATE_ACTCTX_FLAG_FORCE_EARLY_DEACTIVATION
과 함께DeactivateActCtx()
기능을 제공하십시오. IntPtr
인스턴스 유형을UIntPtr
으로 변경하십시오.
불행히도 이러한 옵션은 도움이되지 않았습니다. 전술 한 SEHException
에 직면하지 않고 활성화 컨텍스트를 disestablishing 할 수있는 방법이 있습니까?
UPDATE는
는 가비지 컬렉터의 스레드가 문제의 원인 인 것 같다. GC는 항상 고유 한 스레드로 실행되며 달리 지정하지 않아도됩니다. 이 특정 스레드에서 활성화 컨텍스트 (DeactivateActCtx
)를 비활성화하려고 할 때 후드 아래에서 일종의 액세스 위반이 발생하는 것으로 보입니다. 따라서 각 랩핑 된 호출에서 활성화 컨텍스트를 활성화 및 비활성화하는 것을 제외하고는 이러한 불편 함을 처리 할 수있는 직접적인 방법이 없다고 생각합니다. 그렇지 않으면 증명할 수있는 제안은 언제나 환영합니다.
여기서 파이널 라이저를 사용할 수 없습니다. 잘못된 스레드입니다. –
@HansPassant 이것이 불가능한 이유에 대해 좀 더 자세히 설명해 주시겠습니까? DLL을 언로드 한 후 암시 적으로 활성화 컨텍스트를 정리하기 위해 런타임을 그대로두면 올바르게 작동하지 않습니다. – Aurora
try/finally를 사용하십시오. –