我目前正在开发混合托管/本机工作链,需要为免注册 COM 支持创建激活上下文(请参阅将免注册 COM 清单嵌入到具有本机/托管环境的 C# dll 中)。以下代码片段是 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;
}
}
问题出在pinvoked上DeactivateActCtx()
里面的函数DestroyActivationContext()
方法。一旦被调用,就会出现一个SEHException
is throws:外部组件抛出异常。 0x80004005。
没有可用的错误代码Marshal.GetLastWin32Error()
函数,这将为我提供一些合理的信息。
到目前为止我尝试过的事情:
- 移动
DestroyActivationContext()
从析构函数到函数Dispose
方法,反之亦然。
- 删除
IDisposable
界面一并。
- 将底层 COM 对象的线程模型从 Apartment 更改为 Free。
- 提供
DeactivateActCtx()
功能与DEACTIVATE_ACTCTX_FLAG_FORCE_EARLY_DEACTIVATION
作为输入参数。
- 改变类型
IntPtr
实例到UIntPtr
.
不幸的是,这些选择都没有帮助。是否有任何可能的方法可以在不遇到上述情况的情况下解除激活上下文SEHException
?
UPDATE
看来垃圾收集器的线程是问题的原因。 GC 始终在其自己的独特线程中运行,显然不可能另行指定。当尝试停用激活上下文时,似乎在幕后发生了某种访问冲突(DeactivateActCtx
)来自这个特定的线程。所以我想除了在每个包装调用中激活和停用激活上下文之外,没有直接的方法来处理这种麻烦。任何可以证明并非如此的建议仍然受到欢迎。
为了使其工作,每个包装的调用都需要包含一个激活和随后的停用请求。谢谢大卫赫夫南,谁提供了处理这个问题的合理方法。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)