Jak załadować Assembly do AppDomain ze wszystkimi referencjami rekurencyjnie?

Chcę załadować do nowego AppDomain jakiegoś zestawu, który ma złożone drzewo referencji (MyDll.dll - > Microsoft.Biuro.Interop.Excel.dll - > Microsoft.Vbe.Interop.dll - > Office.dll - > stdole.dll)

O ile dobrze zrozumiałem, kiedy zespół jest ładowany do AppDomain, jego odwołania nie będą ładowane automatycznie, a ja muszę ładować je ręcznie. Więc kiedy to zrobię:

string dir = @"SomePath"; // different from AppDomain.CurrentDomain.BaseDirectory
string path = System.IO.Path.Combine(dir, "MyDll.dll");

AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
setup.ApplicationBase = dir;
AppDomain domain = AppDomain.CreateDomain("SomeAppDomain", null, setup);

domain.Load(AssemblyName.GetAssemblyName(path));

I got FileNotFoundException:

Could not load file or assembly ' MyDll, Version=1.0.0.0, Culture=neutral, Publiceytoken= null ' lub jedna z jego zależności. System nie może znaleźć określonego pliku.

Myślę, że kluczową częścią jest jedna z jego zależności.

Ok, zrobię następny przed domain.Load(AssemblyName.GetAssemblyName(path));

foreach (AssemblyName refAsmName in Assembly.ReflectionOnlyLoadFrom(path).GetReferencedAssemblies())
{
    domain.Load(refAsmName);
}

Ale dostałem FileNotFoundException Ponownie, na innym (odwołanym) zbiorze.

Jak wczytać rekurencyjnie wszystkie referencje?

Czy muszę tworzyć drzewo referencji przed załadowaniem zespołu korzenia? Jak uzyskać referencje zespołu bez ładowania go?

Author: Andry, 2009-03-18

8 answers

Musisz wywołać CreateInstanceAndUnwrap zanim twój obiekt proxy uruchomi się w obcej domenie aplikacji.

 class Program
{
    static void Main(string[] args)
    {
        AppDomainSetup domaininfo = new AppDomainSetup();
        domaininfo.ApplicationBase = System.Environment.CurrentDirectory;
        Evidence adevidence = AppDomain.CurrentDomain.Evidence;
        AppDomain domain = AppDomain.CreateDomain("MyDomain", adevidence, domaininfo);

        Type type = typeof(Proxy);
        var value = (Proxy)domain.CreateInstanceAndUnwrap(
            type.Assembly.FullName,
            type.FullName);

        var assembly = value.GetAssembly(args[0]);
        // AppDomain.Unload(domain);
    }
}

public class Proxy : MarshalByRefObject
{
    public Assembly GetAssembly(string assemblyPath)
    {
        try
        {
            return Assembly.LoadFile(assemblyPath);
        }
        catch (Exception)
        {
            return null;
            // throw new InvalidOperationException(ex);
        }
    }
}

Zauważ również, że jeśli użyjesz LoadFrom prawdopodobnie pojawi się wyjątek FileNotFound, ponieważ program do rozpoznawania złożenia spróbuje znaleźć skład, który ładujesz w GAC lub folderze bin bieżącej aplikacji. Zamiast tego użyj LoadFile, aby załadować dowolny plik złożenia--ale zauważ, że jeśli to zrobisz, będziesz musiał samodzielnie załadować zależności.

 58
Author: Jduv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-13 04:43:50

Http://support.microsoft.com/kb/837908/en-us

Wersja C#:

Stwórz klasę moderatora i Dziedzicz ją z MarshalByRefObject:

class ProxyDomain : MarshalByRefObject
{
    public Assembly GetAssembly(string assemblyPath)
    {
        try
        {
            return Assembly.LoadFrom(assemblyPath);
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException(ex.Message);
        }
    }
}

Call from client site

ProxyDomain pd = new ProxyDomain();
Assembly assembly = pd.GetAssembly(assemblyFilePath);
 16
Author: rockvista,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-06 18:31:45

W nowej aplikacji AppDomain spróbuj ustawić obsługę zdarzenia AssemblyResolve . To zdarzenie zostanie wywołane, gdy brakuje zależności.

 10
Author: David,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-18 14:55:28

Po przekazaniu instancji assembly z powrotem do domeny wywołującej, domena wywołująca spróbuje ją załadować! Dlatego dostajesz wyjątek. Dzieje się tak w ostatniej linijce kodu:

domain.Load(AssemblyName.GetAssemblyName(path));

Tak więc, cokolwiek chcesz zrobić z assembly, powinno być zrobione w klasie proxy - klasie, która dziedziczy MarshalByRefObject .

Policz, że domena wywołująca i nowo utworzona domena powinny mieć dostęp do zestawu klasy proxy. Jeśli twój problem nie jest zbyt skomplikowane, rozważ pozostawienie folderu ApplicationBase bez zmian, więc będzie on taki sam jak folder domeny wywołującej (nowa domena załaduje tylko potrzebne zespoły).

W kodzie prostym:

public void DoStuffInOtherDomain()
{
    const string assemblyPath = @"[AsmPath]";
    var newDomain = AppDomain.CreateDomain("newDomain");
    var asmLoaderProxy = (ProxyDomain)newDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(ProxyDomain).FullName);

    asmLoaderProxy.GetAssembly(assemblyPath);
}

class ProxyDomain : MarshalByRefObject
{
    public void GetAssembly(string AssemblyPath)
    {
        try
        {
            Assembly.LoadFrom(AssemblyPath);
            //If you want to do anything further to that assembly, you need to do it here.
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException(ex.Message, ex);
        }
    }
}

Jeśli musisz załadować zespoły z folderu innego niż bieżący folder domeny aplikacji, Utwórz nową domenę aplikacji z określonym folderem ścieżki wyszukiwania bibliotek DLL.

Na przykład linia tworzenia domeny aplikacji z powyższego kodu powinna zostać zastąpiona z:

var dllsSearchPath = @"[dlls search path for new app domain]";
AppDomain newDomain = AppDomain.CreateDomain("newDomain", new Evidence(), dllsSearchPath, "", true);

W ten sposób wszystkie biblioteki DLL zostaną automatycznie rozwiązane z biblioteki DLL.

 9
Author: Nir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-19 07:59:47

Musisz obsługiwać AppDomain.AssemblyResolve lub AppDomain.ReflectionOnlyAssemblyResolve zdarzenia (w zależności od obciążenia, które robisz) w przypadku, gdy odwołany zespół nie znajduje się w GAC lub na ścieżce sondowania CLR.

AppDomain.AssemblyResolve

AppDomain.ReflectionOnlyAssemblyResolve

 5
Author: Dustin Campbell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-18 14:52:53

Trochę mi zajęło zrozumienie odpowiedzi @user1996230, więc postanowiłem podać bardziej wyraźny przykład. W poniższym przykładzie robię proxy dla obiektu załadowanego w innym AppDomain i wywołuję metodę na tym obiekcie z innej domeny.

class ProxyObject : MarshalByRefObject
{
    private Type _type;
    private Object _object;

    public void InstantiateObject(string AssemblyPath, string typeName, object[] args)
    {
        assembly = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + AssemblyPath); //LoadFrom loads dependent DLLs (assuming they are in the app domain's base directory
        _type = assembly.GetType(typeName);
        _object = Activator.CreateInstance(_type, args); ;
    }

    public void InvokeMethod(string methodName, object[] args)
    {
        var methodinfo = _type.GetMethod(methodName);
        methodinfo.Invoke(_object, args);
    }
}

static void Main(string[] args)
{
    AppDomainSetup setup = new AppDomainSetup();
    setup.ApplicationBase = @"SomePathWithDLLs";
    AppDomain domain = AppDomain.CreateDomain("MyDomain", null, setup);
    ProxyObject proxyObject = (ProxyObject)domain.CreateInstanceFromAndUnwrap(typeof(ProxyObject).Assembly.Location,"ProxyObject");
    proxyObject.InstantiateObject("SomeDLL","SomeType", new object[] { "someArgs});
    proxyObject.InvokeMethod("foo",new object[] { "bar"});
}
 3
Author: grouma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-20 17:41:12

Kluczem Jest Zdarzenie AssemblyResolve wywołane przez AppDomain.

[STAThread]
static void Main(string[] args)
{
    fileDialog.ShowDialog();
    string fileName = fileDialog.FileName;
    if (string.IsNullOrEmpty(fileName) == false)
    {
        AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
        if (Directory.Exists(@"c:\Provisioning\") == false)
            Directory.CreateDirectory(@"c:\Provisioning\");

        assemblyDirectory = Path.GetDirectoryName(fileName);
        Assembly loadedAssembly = Assembly.LoadFile(fileName);

        List<Type> assemblyTypes = loadedAssembly.GetTypes().ToList<Type>();

        foreach (var type in assemblyTypes)
        {
            if (type.IsInterface == false)
            {
                StreamWriter jsonFile = File.CreateText(string.Format(@"c:\Provisioning\{0}.json", type.Name));
                JavaScriptSerializer serializer = new JavaScriptSerializer();
                jsonFile.WriteLine(serializer.Serialize(Activator.CreateInstance(type)));
                jsonFile.Close();
            }
        }
    }
}

static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    string[] tokens = args.Name.Split(",".ToCharArray());
    System.Diagnostics.Debug.WriteLine("Resolving : " + args.Name);
    return Assembly.LoadFile(Path.Combine(new string[]{assemblyDirectory,tokens[0]+ ".dll"}));
}
 3
Author: Leslie Marshall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-19 15:16:18

[14]} musiałem to zrobić kilka razy i zbadałem wiele różnych rozwiązań.

Rozwiązanie, które znajduję w najbardziej eleganckim i łatwym do wykonania, może być wdrożone jako takie.

1. Tworzenie projektu, który można utworzyć prosty interfejs

interfejs będzie zawierał podpisy wszystkich członków, do których chcesz zadzwonić.

public interface IExampleProxy
{
    string HelloWorld( string name );
}

Its important to keep this project clean and lite. Jest to projekt, który zarówno AppDomain's może odwoływać się i pozwoli nam aby nie odwoływać się do Assembly, chcemy załadować domenę seprate z naszego zestawu klienta.

2. Teraz Utwórz projekt, który ma kod, który chcesz załadować w oddzielnym AppDomain.

ten projekt podobnie jak w przypadku Klienta proj będzie odwoływał się do proxy proj i zaimplementujesz interfejs.

public interface Example : MarshalByRefObject, IExampleProxy
{
    public string HelloWorld( string name )
    {
        return $"Hello '{ name }'";
    }
}

3. Następnie w projekcie klienta Wczytaj kod w innym AppDomain.

teraz tworzymy nową AppDomain. Może określić lokalizację bazową dla odniesień do złożenia. Sondowanie sprawdzi, czy w GAC i w bieżącym katalogu oraz w AppDomain Base loc znajdują się zależne zespoły.

// set up domain and create
AppDomainSetup domaininfo = new AppDomainSetup
{
    ApplicationBase = System.Environment.CurrentDirectory
};

Evidence adevidence = AppDomain.CurrentDomain.Evidence;

AppDomain exampleDomain = AppDomain.CreateDomain("Example", adevidence, domaininfo);

// assembly ant data names
var assemblyName = "<AssemblyName>, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null|<keyIfSigned>";
var exampleTypeName = "Example";

// Optional - get a reflection only assembly type reference
var @type = Assembly.ReflectionOnlyLoad( assemblyName ).GetType( exampleTypeName ); 

// create a instance of the `Example` and assign to proxy type variable
IExampleProxy proxy= ( IExampleProxy )exampleDomain.CreateInstanceAndUnwrap( assemblyName, exampleTypeName );

// Optional - if you got a type ref
IExampleProxy proxy= ( IExampleProxy )exampleDomain.CreateInstanceAndUnwrap( @type.Assembly.Name, @type.Name );    

// call any members you wish
var stringFromOtherAd = proxy.HelloWorld( "Tommy" );

// unload the `AppDomain`
AppDomain.Unload( exampleDomain );

jeśli potrzebujesz, istnieje mnóstwo różnych sposobów ładowania zespołu. Możesz użyć innego sposobu z tym rozwiązaniem. Jeśli masz kwalifikowaną nazwę asemblera, to lubię używać CreateInstanceAndUnwrap, ponieważ ładuje bajty asemblera, a następnie tworzy instancje Twojego typu i zwraca object, które możesz łatwo wysłać do swojego typu proxy lub jeśli nie, to do silnie wpisanego Kod możesz użyć środowiska uruchomieniowego dynamic language I przypisać zwrócony obiekt do zmiennej dynamic wpisanej, a następnie bezpośrednio wywołać członków.

Proszę bardzo.

Pozwala to na załadowanie zestawu, do którego twój Klient proj nie ma odniesienia w oddzielnym AppDomain i wywołanie na nim członków od klienta.

Do testowania lubię używać okna modułów w Visual Studio. Pokaże Ci domenę montażu klienta i jakie są w niej załadowane wszystkie moduły domena, a także nowa domena aplikacji i to, jakie zespoły lub moduły są ładowane w tej domenie.

Kluczem jest upewnienie się, że kod pochodzi z MarshalByRefObject lub jest serializowalny.

`MarshalByRefObject pozwoli Ci skonfigurować czas życia domeny. Na przykład, powiedzmy, że chcesz, aby domena została zniszczona, jeśli proxy nie został wywołany w ciągu 20 minut.

Mam nadzieję, że to pomoże.
 0
Author: SimperT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-09 23:47:46