2

これは非常に長いものです。

Core.dll (以下を参照) を参照すると、アプリケーションは type の新しいオブジェクトを作成し、Coreその際、コンストラクターはフォルダーをスキャンして、インターフェイスC:\Problemsを使用する型を含む DLL を探すという考え方です。IPlugin

コンソール アプリケーションから Core.dll を参照すると機能します。Core.dll が GAC にない限り、PowerShell から同等の操作を行っても機能しません。

詳細は次のとおりです。

次の結果であるアセンブリ(Core.dll)があります。

public class Core
{
    List<IPlugin> plugins = new List<IPlugin>();

    public Core(string path)
    {
        LoadPlugins(path);
        foreach (IPlugin plugin in plugins)
            plugin.Work();
    }

    void LoadPlugins(string path)
    {
        foreach (string file in Directory.GetFiles(path))
        {
            FileInfo fileInfo = new FileInfo(file);
            Assembly dllAssembly = Assembly.LoadFile(file);
            foreach (Type type in dllAssembly.GetTypes())
            {
                Type typeInterface = type.GetInterface("Problems.IPlugin");
                if (typeInterface != null)
                    plugins.Add((IPlugin)Activator.CreateInstance(
                                    dllAssembly.GetType(type.ToString())));
            }
        }
    }
}

public interface IPlugin
{
    string Name {get; set;}
    void Work();
}

次に、以下を含む別のアセンブリ MyPlugin.dll があります。

public class MyPlugin : IPlugin
{
    public string Name { get; set; }
    public void Work()
    {
        Console.WriteLine("Hello, World. I'm a plugin!");
    }
}

これをすべて 3 番目のプロジェクト TestApp.exe (参照として Core.dll を使用) でテストしました。

static void Main(string[] args)
{
    Core core = new Core(@"C:\Problems");
    Console.ReadLine();
}

Core.dll と MyPlugin.dll の両方を C:\Problems に配置すると、TestApp.exe が魅力的に機能します。そこで、次の PowerShell スクリプトを作成します。

Add-Type -Path "C:\Problems\Core.dll"
$core = New-Object Problems.Core "C:\Problems"

これが私の結果です (赤の束を想像してください):

New-Object : Exception calling ".ctor" with "1" argument(s): "Unable to load one or more o
f the requested types. Retrieve the LoaderExceptions property for more information."
At line:1 char:11
+ New-Object <<<<  Problems.Core "C:\Problems"
    + CategoryInfo          : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

例外をドリルダウンすると、次のことがわかります。

PS C:\Problems> $Error[0].Exception.InnerException.LoaderExceptions
Could not load file or assembly 'Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

これまでの私の結論は、プラグイン ローダーの一部として GetTypes() を実行しているときに、MyPlugin が Problems.IPlugin を継承していることがわかりますが、どこにあるのかわかりません。でも、すでに Core.dll にロードしていませんか?

これを回避できる唯一の方法は、Core.dll アセンブリに署名して GAC に追加することですが、これは煩わしく、私の目的には合いません。

(すべてのクラスは Problems 名前空間にあり、問題を説明するために大量のクリーンアップとフィルタリングのロジックを省略しました。)

4

1 に答える 1

2

これを解決するには、適切な場所を調べてアセンブリ解決の問題を確実に処理するようにします。つまり、Core クラスを次のように変更します。

public class Core
{
    List<IPlugin> plugins = new List<IPlugin>();
    string path;

public Core(string path)
{
    this.path = path;
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
    LoadPlugins();
    foreach (IPlugin plugin in plugins)
        plugin.Work();
}

Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    var currentAssemblies = AppDomain.CurrentDomain.GetAssemblies();

    foreach (var assembly in currentAssemblies)
    {
        if (assembly.FullName == args.Name)
        {
            Console.WriteLine("resolved assembly " + args.Name + " using exising appdomain");
            return assembly;
        }
    }

    var file = args.Name.Split(new char[] { ',' })[0].Replace(@"\\", @"\");
    var dll = Path.Combine(path, file) + ".dll";
    Console.WriteLine("resolved assembly " + args.Name + " from " + path);
    return File.Exists(dll) ? Assembly.LoadFrom(dll) : null;
}

void LoadPlugins()
{
    foreach (string file in Directory.GetFiles(this.path, "*.dll"))
    {
        FileInfo fileInfo = new FileInfo(file);
        Assembly dllAssembly = Assembly.LoadFile(file);
        foreach (Type type in dllAssembly.GetTypes())
        {
            Type typeInterface = type.GetInterface("Problems.IPlugin");
            if (typeInterface != null)
                plugins.Add((IPlugin)Activator.CreateInstance(
                                dllAssembly.GetType(type.ToString())));
        }
    }
}

}
于 2012-06-08T08:00:33.987 に答える