免注册COM互操作:在终结器中停用激活上下文将引发SEHException [英] Registration-Free COM Interop: Deactivating activation context in finalizer throws SEHException

查看:182
本文介绍了免注册COM互操作:在终结器中停用激活上下文将引发SEHException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在混合管理/本地工作链上工作,并且需要创建一个激活上下文来获得免注册的COM支持(请参阅

I am currently working on a mixed managed / native work chain and need to create an activation context for registration-free COM support (see Embed a Registration-Free COM manifest into a C# dll with native/managed environment). The following snippet is part of a larger class inside a C# DLL, which holds a reference to a COM Wrapper and establishes the required activation context:

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()方法内的pincked DeactivateActCtx()函数.调用后,将立即引发SEHException:外部组件已引发异常. 0x80004005.

The problem lies in the pinvoked DeactivateActCtx() function inside the DestroyActivationContext() method. As soon as it's called, an SEHException is thrown: External component has thrown an exception. 0x80004005.

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

There is no error code available via the Marshal.GetLastWin32Error() function, which would provide me with some reasonable information.

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

Things I have tried so far:

  • DestroyActivationContext()函数从析构函数移至Dispose方法,反之亦然.
  • 完全删除IDisposable界面.
  • 将基础COM对象的线程模型从Apartment更改为Free.
  • 使用DEACTIVATE_ACTCTX_FLAG_FORCE_EARLY_DEACTIVATION作为输入参数提供DeactivateActCtx()函数.
  • IntPtr实例的类型更改为UIntPtr.
  • Moving the DestroyActivationContext() function from the destructor to the Dispose method and vice versa.
  • Removing the IDisposable interface altogether.
  • Changing the threading model of the underlying COM object from Apartment to Free.
  • Providing the DeactivateActCtx() function with DEACTIVATE_ACTCTX_FLAG_FORCE_EARLY_DEACTIVATION as an input argument.
  • Changing the type of the IntPtr instances to UIntPtr.

不幸的是,这些选择都没有帮助.有没有可能在不遇到上述SEHException的情况下取消激活上下文的方法?

Unfortunately, none of these options have helped. Is there any possible way of disestablishing the activation context without being confronted with the aforementioned SEHException?

更新

似乎垃圾收集器的线程是问题的原因. GC始终在其自己的独立线程中运行,没有明显的可能性可以另外指定.尝试从此特定线程取消激活上下文(DeactivateActCtx)时,似乎在后台发生了某种访问冲突.因此,我想除了在每个包装的调用中激活和停用激活上下文之外,没有简单的方法来处理这种麻烦.仍然欢迎提出任何其他建议.

It seems that the thread of the garbage collector is the cause of the problem. The GC always runs in its own distinct thread with no apparent possibility to specify otherwise. There seems to be some sort of access violation going on under the hood when trying to deactivate the activation context (DeactivateActCtx) from this specific thread. So I guess there's no straightforward way to deal with this nuisance, except activating and deactivating the activation context in each wrapped call. Any suggestions which would prove otherwise are still welcome.

推荐答案

为使此工作正常进行,每个包装的调用都必须包含一个激活和随后的取消激活请求.感谢 David Heffernan ,他提供了解决此问题的合理方法.

In order for this to work, each wrapped call needs to be enclosed with an activation and subsequent deactivation request. Thanks to David Heffernan, who provided a reasonable approach to deal with this issue.

这篇关于免注册COM互操作:在终结器中停用激活上下文将引发SEHException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆