ModuleLoadException, когда Thread.CurrentPrincipal установлен с принципалом, содержащим пользовательский IIdentity

У нас есть приложение, размещенное в ASP.NET MVC WebAPI, в котором используется внешняя управляемая библиотека. Эта управляемая библиотека снова использует библиотеку COM. Библиотека COM — это библиотека общего назначения, используемая многими приложениями и в разных версиях, поэтому мы хотим иметь возможность запускать ее параллельно, загружая ее непосредственно из папки bin и не используя традиционную регистрацию COM. Мы достигли этого, добавив файл манифеста и сделав вызов CreateActCtx в kernel32.dll, чтобы зарегистрировать пользовательский контекст активации (код приведен ниже).

Это работает нормально само по себе, однако, когда мы устанавливаем Thread.CurrentPrincipal в экземпляр GenericPrincipal с пользовательской реализацией IIdentity до загрузки библиотеки COM, мы получаем <CrtImplementationDetails>.ModuleLoadException с сообщением «Не удалось загрузить модуль C ++ при попытке инициализировать домен приложения по умолчанию.». Он имеет внутреннюю исключительную ситуацию System.Runtime.Serialization.SerializationException, в которой говорится, что он не может загрузить сборку, которая изначально инициировала загрузку сборки COM. Трассировка стека:

Server stack trace:
at System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetType(BinaryAssemblyInfo assemblyInfo, String name)
at System.Runtime.Serialization.Formatters.Binary.ObjectMap..ctor(String objectName, String[] memberNames, BinaryTypeEnum[] binaryTypeEnumA, Object[] typeInformationA, Int32[] memberAssemIds, ObjectReader objectReader, Int32 objectId, BinaryAssemblyInfo assemblyInfo, SizedArray assemIdToAssemblyTable)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryObjectWithMapTyped record)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Remoting.Channels.CrossAppDomainSerializer.DeserializeObject(MemoryStream stm)
at System.Runtime.Remoting.Messaging.SmuggledMethodCallMessage.FixupForNewAppDomain()
at System.Runtime.Remoting.Channels.CrossAppDomainSink.DoDispatch(Byte[] reqStmBuff, SmuggledMethodCallMessage smuggledMcm, SmuggledMethodReturnMessage& smuggledMrm)
at System.Runtime.Remoting.Channels.CrossAppDomainSink.DoTransitionDispatchCallback(Object[] args)

Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at System.AppDomain.get_Id()
at <CrtImplementationDetails>.DoCallBackInDefaultDomain(IntPtr function, Void* cookie)
at <CrtImplementationDetails>.LanguageSupport._Initialize(LanguageSupport* )
at <CrtImplementationDetails>.LanguageSupport.Initialize(LanguageSupport* )
--- End of inner exception stack trace ---
at <CrtImplementationDetails>.LanguageSupport.Initialize(LanguageSupport* )
at .cctor()
--- End of inner exception stack trace ---

Когда мы не устанавливаем Thread.CurrentPrincipal или используем GenericPrincipal с GenericIdentity внутри, он работает нормально. На данный момент решение с GenericIdentity является для нас обходным путем, поскольку мы в основном устанавливаем Thread.CurrentPrincipal для использования аутентификации ASP.NET MVC.

Следующий код работает:

Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity("bla"), new string[0]);

…в то время как следующее не делает:

Thread.CurrentPrincipal = new GenericPrincipal(new CustomIdentity("bla"), new string[0]);

Ниже приведен код global.asax, используемый для регистрации пользовательского контекста активации, ссылающегося на папку bin и файл манифеста для выполнения reg-free COM:

public class Global : System.Web.HttpApplication
{
private const UInt32 ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET = 14011;
IntPtr hActCtx;
private static readonly ILogger s_logger = LogManager.GetCurrentClassLogger();

protected void Application_Start(object sender, EventArgs e)
{
// Required files are copied in host projects post-build event
// NB! you can only set the default activation context like this once per app domain,
//  so if you need multiple apps/services using side-by-side they cannot share app pool.

UInt32 dwError = 0;
string binDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"bin");
string manifestPath = Path.Combine(binDirectory, "Iit.OpenApi.Services.ReferenceData.manifest");
var activationContext = new ACTCTX();
activationContext.cbSize = Marshal.SizeOf(typeof(ACTCTX));
activationContext.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT | ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID;
activationContext.lpSource = manifestPath;
activationContext.lpAssemblyDirectory = binDirectory;
hActCtx = CreateActCtx(ref activationContext);
if (hActCtx.ToInt32() == -1)
{
dwError = (UInt32)Marshal.GetLastWin32Error();
}

if ((hActCtx.ToInt32() == -1) && (dwError != Global.ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET))
{
var err = string.Format("Cannot create process-default win32 sxs context, error={0} manifest={1}", dwError, manifestPath);
throw new ApplicationException(err);
}
// TODO: add eventid to enum
if ((hActCtx.ToInt32() == -1) && (dwError == Global.ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET))
{
GetCurrentActCtx(out hActCtx);
s_logger.Info(9998, string.Format("Re-using exising ActivationContext [{0}].", hActCtx.ToInt64()));
}
else
{
s_logger.Info(9998, string.Format("ActivationContext [{0}] created successfully.", hActCtx.ToInt64()));
}

}

protected void Application_End()
{
if (hActCtx.ToInt64() != INVALID_HANDLE_VALUE)
{
ReleaseActCtx(hActCtx);
// TODO: add eventid to enum
s_logger.Info(9999, string.Format("ActivationContext [{0}] released successfully.", hActCtx.ToInt64()));
}
}

[StructLayout(LayoutKind.Sequential)]
private struct ACTCTX
{
public int cbSize;
public uint dwFlags;
public string lpSource;
public ushort wProcessorArchitecture;
public ushort wLangId;
public string lpAssemblyDirectory;
public string lpResourceName;
public string lpApplicationName;
}

private const uint ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x004;
private const uint ACTCTX_FLAG_SET_PROCESS_DEFAULT = 0x010;
private const Int64 INVALID_HANDLE_VALUE = -1;

[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateActCtx(ref ACTCTX actctx);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetCurrentActCtx(out IntPtr hActCtx);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern void ReleaseActCtx(IntPtr hActCtx);

}

Любой вклад в то, что идет не так при использовании пользовательского IIdentity, и как я могу заставить его работать с reg-free COM?

1

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector