2014-10-22 9 views
1

현재 관리되는 네이티브 워크 체인이 혼합되어 있으므로 등록이 필요없는 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)를 비활성화하려고 할 때 후드 아래에서 일종의 액세스 위반이 발생하는 것으로 보입니다. 따라서 각 랩핑 된 호출에서 활성화 컨텍스트를 활성화 및 비활성화하는 것을 제외하고는 이러한 불편 함을 처리 할 수있는 직접적인 방법이 없다고 생각합니다. 그렇지 않으면 증명할 수있는 제안은 언제나 환영합니다.

+1

여기서 파이널 라이저를 사용할 수 없습니다. 잘못된 스레드입니다. –

+0

@HansPassant 이것이 불가능한 이유에 대해 좀 더 자세히 설명해 주시겠습니까? DLL을 언로드 한 후 암시 적으로 활성화 컨텍스트를 정리하기 위해 런타임을 그대로두면 올바르게 작동하지 않습니다. – Aurora

+0

try/finally를 사용하십시오. –

답변

0

이 작업을 수행하려면 각 랩핑 된 호출을 활성화 및 후속 비활성화 요청으로 묶어야합니다. David Heffernan 덕분에 누가이 문제를 해결할 합리적인 방법을 제공했는지 알 수 있습니다.