2014-10-22 4 views
1

В настоящее время я работаю над смешанной управляемой/собственной рабочей цепочкой и вам нужно создать контекст активации для бесплатной поддержки COM (см. Embed a Registration-Free COM manifest into a C# dll with native/managed environment). Следующий фрагмент является частью более широкого класса внутри C# DLL, который содержит ссылку на COM Wrapper и устанавливает необходимый контекст активации:Без регистрации COM Interop: деактивация контекста активации в finalizer throws 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; 
    } 
} 

проблема заключается в pinvoked DeactivateActCtx() функции внутри метода DestroyActivationContext(). Как только он вызывается, вызывается SEHException: Внешний компонент выбрасывает исключение. 0x80004005.

Код ошибки отсутствует через функцию Marshal.GetLastWin32Error(), которая предоставит мне некоторую разумную информацию.

Вещей я пытался до сих пор:

  • Перемещение функции DestroyActivationContext() из деструктора методы Dispose и наоборот.
  • Извлечение интерфейса IDisposable.
  • Изменение модели резьбы базового COM-объекта из квартиры в свободную.
  • Предоставление функции DeactivateActCtx() с использованием DEACTIVATE_ACTCTX_FLAG_FORCE_EARLY_DEACTIVATION в качестве входного аргумента.
  • Изменение типа IntPtr экземпляров на UIntPtr.

К сожалению, ни один из этих вариантов не помог. Существует ли какой-либо возможный способ устранения контекста активации, не сталкиваясь с вышеупомянутым SEHException?

UPDATE

кажется, что поток сборщика мусора является причиной проблемы. GC всегда работает в своем отдельном потоке, без видимой возможности указать иначе. Кажется, что какое-то нарушение прав доступа происходит под капотом при попытке деактивировать контекст активации (DeactivateActCtx) из этой конкретной темы. Поэтому я думаю, что нет простого способа справиться с этой неприятностью, кроме активации и деактивации контекста активации в каждом завернутом вызове. Любые предложения, которые докажут обратное, по-прежнему приветствуются.

+1

Вы не можете использовать финализатор здесь, неверную нить. –

+0

@ HansPassant Не могли бы вы немного рассказать о том, почему это невозможно? Оставляя это до времени выполнения, чтобы неявно очистить контекст активации после того, как DLL выгрузится, просто не кажется правильным. – Aurora

+0

Используйте try/finally –

ответ

0

Для того, чтобы это сработало, каждый завернутый вызов должен быть заключен с активацией и последующим деактивационным запросом. Благодаря David Heffernan, который предоставил разумный подход для решения этой проблемы.

 Смежные вопросы

  • Нет связанных вопросов^_^