0

サードパーティのプラグインを許可する必要があるプロジェクトに取り組んでいます。以前にプラグインを使用したことがありますが、問題はありませんでした。

私の問題は、WPFがAssembly.LoadFile(file)とActivator.CreateInstance(t)を使用するのを好まないためだと確信しています!

私が遭遇したエラーは次のとおりです。

The component 'Servus.Forms.MainWindow' does not have a resource identified by the URI '/Servus;component/forms/mainwindow.xaml'.

これは MainForm コンストラクターの InitializeComponent(); に表示されます。

MainForm をロードした後にプラグインをロードすると、問題なくロードされますが、他のフォームを開くと (私のアプリケーションには多くあります)、同じ問題が発生しますが、その特定のフォームに関連するエラーが発生します。

また、次のように独自の AppDomain にプラグインをロードしようとしました。

PluginDomain temp = new PluginDomain();
PluginBase tempPlug = temp.GetPlugin(file);

次のクラスを使用します。

public class PluginDomain
{
    public AppDomain CurrentDomain { get; set; }
    public ServusAssemblyLoader CurrentAssemblyLoader { get; set; }

    private readonly Random _rand = new Random();

    public PluginDomain()
    {
    }


    public PluginBase GetPlugin(string assemblyName)
    {
        try
        {
            string appBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            var ads = new AppDomainSetup { ApplicationBase = appBase, PrivateBinPath = appBase, ShadowCopyFiles = "true" };
            CurrentDomain = AppDomain.CreateDomain("ServusDomain_Plugin_" + _rand.Next(0, 100000), null, ads);

            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
            CurrentAssemblyLoader = (ServusAssemblyLoader)
            CurrentDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(ServusAssemblyLoader).FullName);

            AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;

            return CurrentAssemblyLoader.Load(assemblyName);
        }
        catch (Exception e)
        {
            CConsole.WriteLine("Error: " + e.Message);
        }
        finally
        {
            CurrentAssemblyLoader = null;
            AppDomain.Unload(CurrentDomain);
        }

        return null;
    }

    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        string[] parts = args.Name.Split(',');
        string file = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\" + parts[0].Trim() + ".dll";

        return Assembly.LoadFrom(file);
    }
}

public class ServusAssemblyLoader : MarshalByRefObject, IAssemblyLoader
{
    public PluginBase Load(string file)
    {
        Assembly asm = Assembly.LoadFrom(file);

        foreach (Type t in asm.GetTypes())
        {
            if (t.IsSubclassOf(typeof(PluginBase)))
            {
                return (PluginBase)Activator.CreateInstance(t);
            }
        }

        return null;
    }
}

public interface IAssemblyLoader
{
    PluginBase Load(string file);
}

これは、次のような TransparentProxy オブジェクトを返します。

{System.Runtime.Remoting.Proxies.__TransparentProxy}

ただし、PluginBase オブジェクトを返すことを期待していたので、これを使用する方法がわかりません。

多くの人がこの問題を抱えていることを読みました。新しい AppDomain を使用するようにという回答がありますが、ご覧のとおり、これは今のところ役に立ちません。

十分な情報を提供できたと思いますが、誰か助けてもらえますか?

4

1 に答える 1

0

PluginDomainクラスにいくつか問題があったことがわかりました。

修正#1:

交換:

return (PluginBase)Activator.CreateInstance(t);

と:

(PluginBase)asm.CreateInstance(t.ToString());

修正#2:

削除する:

AppDomain.Unload(CurrentDomain);

修正#3 :(純粋にデバッグ用)

交換:

return CurrentAssemblyLoader.Load(assemblyName);

と:

PluginBase obj = CurrentAssemblyLoader.Load(assemblyName);
return obj;

編集:

新しいAppDomainは古いAppDomainのオブジェクトにアクセスできないことに注意してください。だから私の問題は半分しか修正されていません。

于 2013-02-21T18:01:08.893 に答える