簡體   English   中英

免注冊COM互操作:在終結器中停用激活上下文將引發SEHException

[英]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接口。
  • 將基礎COM對象的線程模型從Apartment更改為Free。
  • 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM