1

外部マネージド ライブラリを使用する ASP.NET MVC WebAPI でホストされているアプリケーションがあります。このマネージ ライブラリでも、COM ライブラリが使用されます。COM ライブラリは、さまざまなバージョンの多くのアプリケーションで使用される汎用ライブラリであるため、従来の COM 登録を使用せずに、bin フォルダーから直接ロードすることで、サイド バイ サイドで実行できるようにしたいと考えています。マニフェスト ファイルを追加し、kernel32.dll で CreateActCtx を呼び出してカスタム アクティベーション コンテキストを登録することで、これを実現しました (コードは以下に含まれています)。

これ自体は正常に動作していますが、COM ライブラリがロードされる前にカスタム IIdentity 実装を使用して Thread.CurrentPrincipal を GenericPrincipal インスタンスに設定すると、<CrtImplementationDetails>.ModuleLoadException が発生し、「試行中に C++ モジュールをロードできませんでした」というメッセージが表示されます。デフォルトのアプリドメインを初期化します。」. 最初にCOMアセンブリのロードを実際に開始したアセンブリをロードできないことを示す内部System.Runtime.Serialization.SerializationExceptionがあります。スタックトレースは次のとおりです。

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 を設定しないか、内部に GenericIdentity を持つ GenericPrincipal を使用しないと、正常に動作します。今のところ、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]);

以下は、reg-free COM を実行するために bin フォルダーとマニフェスト ファイルを参照するカスタム アクティベーション コンテキストを登録するために使用される global.asax コードです。

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 で動作させるにはどうすればよいですか?

4

0 に答える 0