获取在应用程序域中启动的进程的程序集名称

本文关键字:进程 程序集 启动 应用程序域 获取 | 更新日期: 2023-09-27 18:17:49

我有一个创建应用程序域并启动它的服务:

this._appDomain = AppDomain.CreateDomain(this._appName, AppDomain.CurrentDomain.Evidence, appDomainSetup);
this._startStopControllerToRun = (IStartStop)this._appDomain.CreateInstanceFromAndUnwrap(assemblyName, this._fullyQualifiedClassName);
this._startStopControllerToRun.Start();

已经运行很长时间了。问题是当控制器在这个应用程序域中启动时,调用框架日志记录类。记录器获取条目程序集名称,并将其作为源记录在事件日志中。这就是记录器获取源(调用者)名称的方式:

private static string GetSource()
{
    try
    {
        var assembly = Assembly.GetEntryAssembly();
        // GetEntryAssembly() can return null when called in the context of a unit test project.
        // That can also happen when called from an app hosted in IIS, or even a windows service.
        if (assembly == null)
        {
            // From http://stackoverflow.com/a/14165787/279516:
            assembly = new StackTrace().GetFrames().Last().GetMethod().Module.Assembly;
        }
        return assembly.GetName().Name;
    }
    catch
    {
        return "Unknown";
    }
}

在添加if检查之前,记录器将为源记录"Unknown"。经过一番研究,我在if块中添加了该尝试。现在,记录器将"mscorlib"记录为源(条目程序集名称)。

这是概述:主机->控制器(运行在应用域内)

如何获得在域中运行的程序集(具有控制器的程序集)的名称?

注意:我也尝试过这个(下面),但它给了我日志类存在的框架的名称(不是控制器在应用程序域中运行的程序集的名称):

assembly = Assembly.GetExecutingAssembly();

获取在应用程序域中启动的进程的程序集名称

这可能是您想要的一种方法。我在这里演示的是通过SetDataGetData方法将元数据传递和接收到已创建的AppDomain,因此请忽略我如何创建实际的远程类型。

using System;
using System.Reflection;
namespace ConsoleApplication13
{
    class Program
    {
        static void Main(string[] args)
        {
            AppDomain appDomain = AppDomain.CreateDomain("foo");
            appDomain.SetData(FooUtility.SourceKey, FooUtility.SourceValue);
            IFoo foo = (IFoo)appDomain.CreateInstanceFromAndUnwrap(Assembly.GetEntryAssembly().Location, typeof(Foo).FullName);
            foo.DoSomething();
        }
    }
    public static class FooUtility
    {
        public const string SourceKey = "Source";
        public const string SourceValue = "Foo Host";
    }
    public interface IFoo
    {
        void DoSomething();
    }
    public class Foo : MarshalByRefObject, IFoo
    {
        public void DoSomething()
        {
            string source = AppDomain.CurrentDomain.GetData(FooUtility.SourceKey) as string;
            if (String.IsNullOrWhiteSpace(source))
                source = "some default";
            Console.WriteLine(source);
        }
    }
}
输出:


Foo主机按任意键继续…

在你的例子中,你可以将任何源元数据传递给AppDomain:

this._appDomain = AppDomain.CreateDomain(this._appName, AppDomain.CurrentDomain.Evidence, appDomainSetup);
this._appDomain.SetData("Source", "MyController");
this._startStopControllerToRun = (IStartStop)this._appDomain.CreateInstanceFromAndUnwrap(assemblyName, this._fullyQualifiedClassName);
this._startStopControllerToRun.Start();

并在GetSource方法中检查它是否存在。

private static string GetSource()
{
    try
    {
        string source = AppDomain.CurrentDomain.GetData("Source") as string;
        if (!String.IsNullOrWhiteSpace(source))
            return source;
        var assembly = Assembly.GetEntryAssembly();
        // GetEntryAssembly() can return null when called in the context of a unit test project.
        // That can also happen when called from an app hosted in IIS, or even a windows service.
        if (assembly == null)
        {
            // From http://stackoverflow.com/a/14165787/279516:
            assembly = new StackTrace().GetFrames().Last().GetMethod().Module.Assembly;
        }
        return assembly.GetName().Name;
    }
    catch
    {
        return "Unknown";
    }
}

更新替代

您还可以声明一个公共接口方法,用于将源设置在目标域中的静态位置。

using System;
using System.Reflection;
namespace ConsoleApplication13
{
    class Program
    {
        static void Main(string[] args)
        {
            AppDomain appDomain = AppDomain.CreateDomain("foo");
            IFoo foo = (IFoo)appDomain.CreateInstanceFromAndUnwrap(Assembly.GetEntryAssembly().Location, typeof(Foo).FullName);
            foo.SetSource("Foo Host");
            foo.DoSomething();
        }
    }
    public interface IFoo
    {
        void DoSomething();
        void SetSource(string source);
    }
    public class Foo : MarshalByRefObject, IFoo
    {
        public void DoSomething()
        {
            string source = Foo.Source;
            if (String.IsNullOrWhiteSpace(source))
                source = "some default";
            Console.WriteLine(source);
        }
        public static string Source{get; private set;}
        public void SetSource(string source)
        {
            Foo.Source = source;
        }
    }
}

我遇到了一种情况,在。net代码的某个地方,它依赖于Assembly.GetEntryAssembly()。它将接受返回的程序集并检查其程序集级别属性。如果代码在app域中,这将会失败。

长话短说,我不得不解决同样的问题。解决方案是丑陋的,我讨厌我需要这样做,但它工作…

如果你阅读了这里的文档- Assembly.GetEntryAssembly() Method

它包含以下部分:

类型:System.Reflection.Assembly

程序集,该程序集是默认应用程序域中的进程可执行程序,或AppDomain.ExecuteAssembly执行的第一个可执行文件。可以从非托管代码调用时返回null。

为了解决这个问题,我在我的exe中添加了一些代码,使进程退出"/initializingappdomain"作为参数传递。

这里有一些代码来做这个…

// 1. Create your new app domain...
var newDomain = AppDomain.CreateDomain(...);
            
// 2. call domain.ExecuteAssembly, passing in this process and the "/initializingappdomain" argument which will cause the process to exit right away
newDomain.ExecuteAssembly(GetProcessName(), new[] { "/initializingappdomain" });
private static string GetProcessName()
{
   return System.IO.Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName.Replace(".vshost", ""));
}
// 3. Use your app domain as you see fit, Assembly.GetEntryAssembly will now return this hosting .net exe.

同样,这远非理想。如果可以避免这种情况,有更好的解决方案,但如果您发现自己处于不拥有依赖Assembly.GetEntryAssembly()的代码的情况下,这将使您在紧要关头。