В настоящее время я работаю над смешанной управляемой/собственной рабочей цепочкой и вам нужно создать контекст активации для бесплатной поддержки 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
) из этой конкретной темы. Поэтому я думаю, что нет простого способа справиться с этой неприятностью, кроме активации и деактивации контекста активации в каждом завернутом вызове. Любые предложения, которые докажут обратное, по-прежнему приветствуются.
Вы не можете использовать финализатор здесь, неверную нить. –
@ HansPassant Не могли бы вы немного рассказать о том, почему это невозможно? Оставляя это до времени выполнения, чтобы неявно очистить контекст активации после того, как DLL выгрузится, просто не кажется правильным. – Aurora
Используйте try/finally –