Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/272.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 免注册COM互操作:在终结器中停用激活上下文会引发SEHException_C#_Pinvoke_Com Interop_Regfreecom_Activation Context Api - Fatal编程技术网

C# 免注册COM互操作:在终结器中停用激活上下文会引发SEHException

C# 免注册COM互操作:在终结器中停用激活上下文会引发SEHException,c#,pinvoke,com-interop,regfreecom,activation-context-api,C#,Pinvoke,Com Interop,Regfreecom,Activation Context Api,我目前正在处理一个混合的托管/本机工作链,需要为免注册COM支持创建一个激活上下文(请参阅)。以下代码段是C#DLL中较大类的一部分,该类包含对COM包装的引用,并建立所需的激活上下文: using System; using System.Runtime.InteropServices; using System.Diagnostics; namespace FirstClient { public class FirstClientDLL : IDisposable {

我目前正在处理一个混合的托管/本机工作链,需要为免注册COM支持创建一个激活上下文(请参阅)。以下代码段是C#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()
方法中的pinvoked
DeactivateACTCx()
函数。一旦调用它,就会抛出一个
SEHException
:外部组件抛出了一个异常。0x80004005

通过
Marshal.GetLastWin32Error()
函数没有可用的错误代码,这将为我提供一些合理的信息

到目前为止我已经尝试过的事情:

  • DestroyActivationContext()
    函数从析构函数移动到
    Dispose
    方法,反之亦然
  • 完全删除
    IDisposable
    接口
  • 将基础COM对象的线程模型从单元更改为自由
  • 提供带有
    DEACTIVATE\u ACTCTX\u FLAG\u FORCE\u EARLY\u DEACTIVATION的
    deactivateActtx()
    函数作为输入参数
  • IntPtr
    实例的类型更改为
    UIntPtr
不幸的是,所有这些选择都没有帮助。是否有任何可能的方法可以在不面对前面提到的
SEHException
的情况下解除激活上下文的建立

更新


垃圾收集器的线程似乎是问题的原因。GC总是在它自己的独立线程中运行,显然不可能指定其他线程。当尝试从此特定线程中停用激活上下文(
DeactivateActCtx
)时,似乎存在某种访问冲突。所以我想除了在每个包装调用中激活和停用激活上下文之外,没有直接的方法来处理这种麻烦。如果有任何建议证明不是这样,我们仍然欢迎。

为了实现这一点,每个包装好的调用都需要附上一个激活和随后的停用请求。感谢,他提供了一个合理的方法来处理这个问题。

你不能在这里使用终结器,错误的线程。@HansPassant你能详细解释一下为什么这是不可能的吗?让运行时在卸载DLL后隐式清理激活上下文是不对的。@DavidHeffernan这只会掩盖问题。我想找出一种方法,以一种干净的方式停用激活上下文,这是没有例外的。一点也不。我说最后没赶上。最后一块确保你以确定的方式完成。不,你仍然不明白我的意思。显然,您不希望抛出异常。因此,请确保从激活的线程中停用。试一下/最后一次。在伪代码中,
激活;试试{do stuff}finally{deactivate;}
最后一步是确保执行停用。因为你不再依赖GC了。在处理非托管资源时,try/finally是您的首选技术。