创建使用可重载插件的WPF应用程序... [英] Create WPF Application that uses Reloadable Plugins...

查看:105
本文介绍了创建使用可重载插件的WPF应用程序...的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

大家好,

我手头有一个有趣的问题.

我需要创建一个WPF应用程序,它将承载多个UserControls. (应用程序正是这样做的-充当具有UserControls对接功能的容器)

该应用程序还将托管UserControls的数据模型,作为另一个热插拔组件. (我计划为每个组件使用内部队列,以便可以交换组件.)

这些用户控件必须在运行时是可插入的(这很容易),并且必须在运行时可重新加载(这似乎很困难/不可能).(AppDomains似乎是答案,但是除了AppDomain之外,我不知道其他限制) MarshalByValue和marshalByRefObject的限制是否有GUI限制?)在运行时可以像在应用程序启动时热交换UI组件中那样重载".

我还希望UserControl上的Context菜单具有Host中的某些项目和UserControl本身中的某些项目.

我还有逻辑来无缝控制主机和用户控件之间的焦点(在焦点事件上更改边框颜色等)(跨主机和UserControls滚动).

顺便说一句,我还希望对UserControl启用ActiveX,以便与Excel等ActiveX容器集成.

这将使我在自己运行UserControl(单元测试),在容器中运行(无缝导航以及与容器和其他相关UserControl的集成)以及作为与ActiveX容器集成的ActiveX控件方面具有灵活性.
我想知道是否有可能,是否有支持此功能的frameworks(MAF?).任何指向示例或文档的指针都将有所帮助.
预先谢谢您,

此致Ven.

Hello Everybody,

I have an interesting problem at hand.

I have a need to create a WPF Application which will host several UserControls. (The application does just that - serves as a container with docking abilities for UserControls)

The application will also host the data model for the UserControls as another hot Swappable component. (I plan to use internal queues for each Component so that Components can be swapped.)

These user controls will have to be pluggable (This is Easy), and reloadable (This seems to be difficult/ impossible) at run time.(AppDomains seem to be the answer here, But I don''t know the limitations other than AppDomain limitations on MarshalByValue and marshalByRefObject Are there any GUI limitations? ) "Reloadable" at runtime as in Hot Swap the UI component while the app is UP.

I also want the Context menu on a UserControl to have some items from Host and some Items from the UserControl itself.

I also have logic to control Focus (Change Border Color etc. on Focus Events) between the host and UserControls seamlessly (Tabbing across Host and UserControls).

As an aside, I would also want the UserControl to be ActiveX enabled for integration with ActiveX containers like Excel.

This will give me flexibility in terms of running the UserControl by itself (Unit test), In a container (seamless Navigation and integration with container and other relevant UserControls), and as ActiveX control to integrate with ActiveX containers.

I would like to know if it is possible, and if there are frameworks(MAF?) that supports this. Any pointers to examples or documentation will help.
Thank you in advance,

Regards, Ven.

推荐答案

这不是问题.您甚至可以托管和插入整个WPF应用程序.我认为您不需要这个,所以我将解释插件设计的框架.

假设您拥有一个永不更改的主机应用程序(更确切地说,其插件支持子系统未更改或已增量更改,请参见下文),并且有几个插件可以更改插件并开发新的插件.在运行开始时即插即用很容易,可以重新加载,但难度更大;这里的问题不是装载,而是卸载,因为无法卸载组件.您只能卸载应用程序域.

1)首先,应避免按名称匹配任何类型的成员.这是完全有可能的.您需要开发一个插件界面.定义接口类型并记住,从开始开发插件实现的那一刻起,您就永远不要更改.更确切地说,您可以使接口可升级.为此,您可以在派生的接口中添加其他功能.换句话说,您需要保持接口更改增量(切勿删除方法).插件接口将是您与插件签订的合同.

2)使宿主应用程序和插件均可访问您的插件接口的定义.它看起来很琐碎,因为您使用的是主机和所有插件都引用的单独程序集.你可以做得更好!您可以在应用程序中保留要由插件使用的接口和所有帮助程序类型.这将帮助您保持一致的版本控制(没有不同的程序集-没有不同的版本).并非所有人都知道您可以在另一个程序集中将整个应用程序(* .exe)引用为任何DLL.本质上,.NET不知道EXE和DLL之间的区别(与本机Windows相反).

3)现在,您需要在从文件加载的插件程序集中识别一个插件.您可以遍历组装中的所有顶级类型,并检查它们中的任何一个都支持您的插件接口之一.琐碎的?如果有多个类支持插件接口,该怎么办?如果班级太多怎么办?你可以做得更好!创建一个程序集的自定义属性,该属性有助于声明某种支持插件接口的类型.每个插件都应具有以下内容:
This is not a problem. You can even host and plug-in whole WPF applications. I think you don''t need this, so I''ll explain a skeleton of plug-in design.

Let''s assume you have host application which never changes (more exactly, its plug-in support sub-system is not changed or is changed incrementally, see below) and several plug-ins with possibility of changing plug-ins and developing new plug-ins. Pluggable at the very beginning of run-time is easy, reloadable is possible but way more difficult; the problem here is not loading, but unloading, because unloading of assembly is impossible; you can only unload Application Domain.

1) First, you should avoid matching of any type of member by name. This is quite possible. You need to develop a plug-in interface. Define interface type and keep in mind you should never change is from the moment you start developing plug-in implementations. More exactly, you can make interfaces upgradeable. For this purpose, you can add additional functionality in the derived interfaces. In other words, you need to keep interface changed incremental (never remove methods). The plug-in interface(s) will be your contract with plug-ins.

2) Make the definition(s) of your plug-in interfaces(s) accessible to both he host application and plug-in. It looks trivial, because you do it using a separate assembly referenced by both host and all plug-ins. You can do better! You can keep the interfaces and all helper types to be used by plug-ins in your application. It will help you to keep consistent versioning (no different assemblies — no different versions). Not everyone knows that you can reference your whole application (*.exe) in another assembly as any DLL. Essentially, .NET does not know difference between EXE and DLL (in contrast to native Windows).

3) Now, you need to recognize a plug-in in the plug-in assembly loaded from file. You can do it traversing all top-level types in assembly and checking is any of them support one of your plug-in interfaces. Trivial? And what if more than one class supports the plug-in interface? What if there are too many classes? You can do better! Create a custom attribute of assembly which helps to claim some type supporting a plug-in interface. Every plug-in should have something like this:
[assembly: ExposePlugin(
    typeof(TextProcessor),
    typeof(MyDataImporter),
    typeof(MyDataExporter))]


(以防万一,这个样本不清楚:类型ExposePluginInterface不存在,建议您进行开发.)
这样,您的主机插件支持将不会遍历所有程序集类型,而不会遍历ExposePluginInterface的所有那些.

4)一个有趣的插件概念:每个插件都实现插件接口,并将实现传递给主机;并且主机将所需的主机资源传递给插件.您可以使用这些资源来实现插件. (这是对项目(1)的注释.)现在,您可以实现插件并将其加载到主机中.问题出在哪里:如何卸载它?

5)卸载插件要困难得多.您应该了解应用程序域具有完全隔离的数据.您不能简单地将控件从一个AppDomain添加到另一个.应用程序域只能通过IPC进行通信.但是,可以使用IPC的高度简化形式,请参见System.AppDomain.SetDataSystem.AppDomain.GetData.传递任何包含引用数据的数据时会出现问题,因为引用仅在源AppDomain中有效,因此您只能在另一端组装深度克隆.因此,您几乎无法在此边界之间传递UI控件.
这是一个主意:您不应尝试在AppDomains之间共享控制数据.您可以开发仅包含有关如何在宿主站点上组装控件的说明的元数据.使您的插件界面更具语义.当然,您始终可以在两个ApplicationDomains之间共享代码,并通过方法参数传递数据(但只能添加数据).

6)有关如何在新创建的AppDomain:
上下文中运行代码的信息,请参见 AppDomain拒绝加载程序集 [


(Just in case this sample is not clear: the type ExposePluginInterface does not exist, I suggest you develop it.)
In this way, your host plug-in support does not traverse all assembly types but only those listed in the isntance of ExposePluginInterface.

4) A plug-in concept in interesting: each plug-in implements plug-in interface and passes implementation to the host; and host passes required host resources to plug-in. You implement the plug-in using those resources. (This is a note to item (1).) Now you can implement plug-ins and load them in the host. It leaves for the problem: how can you unload it?

5) Unloading of plug-ins is much more difficult. You should understand that Application Domains have totally isolated data. You cannot simply add control from one AppDomain to another. Application Domains can only communication through IPC. However, you can use highly simplified form of IPC, see System.AppDomain.SetData, System.AppDomain.GetData. There is a problem passing any data containing reference data, as the reference is only valid in the source AppDomain, so you can only assembly a deep clone on the other end. So, you hardly can pass UI controls between this boundary.
Here is one idea: you should not try to share control data between AppDomains. You can develop meta-data which only contains the instructions on how to assembly the controls on the host site. Make you plug-in interfaces more semantic. Of course, you can always share code between two ApplicationDomains and pass data (but only add data) through the method parameters.


6) See this on how to run the code in the context of the newly created AppDomain:
AppDomain refuses to load an assembly[^]
The idea is using System.AppDomain.DoCallBack or (less likely as it required the plugin-in with the entry point (EXE file) System.AppDomain.ExecuteAssembly.
[END EDIT]

You don''t have to develop whole plug-in architecture is this way. Alternatively, you can take the other way around. In place of plug-in you can develop separate applications, each representing semantics of what you now consider a plug-in. The plug-ins will be your UI presentation libraries. Imagine you create several Presentation Foundations on your own, but simple and specialized. In particular, you should know how to run a WPF application in a library. If you''re interested, ask another Question, but the answer might be a whole separate article. I actually successfully used both ways; which one to take depends on your ultimate goals.

Good luck,

—SA
P.S.: Please see another Answer for follow-up discussion.


回答后续问题:

问题1


考虑以下示例:
Answering follow-up Questions:

Question 1


Consider the following sample:
//plugin-code of the implemented plug-in interface:

Control[] IControlPlugin.AddControls(DockPanel parent) {
    Button leftRect = new Button();
    leftRect.Content = "_Left";
    DockPanel.SetDock(leftRect, Dock.Left);
    parent.Children.Add(leftRect);
    Button rightRect = new Button();
    rightRect.Content = "_Right";
    DockPanel.SetDock(rightRect, Dock.Right);
    parent.Children.Add(rightRect);
    return new Control[] { leftRect, rightRect, };
} //AddControls

//host code:
Controls[] pluginControls = MyPlugin.AddControls(MyHostDockPanel);


插件在某些UIElement集合上实现操作,并添加了一些新控件.如果从其他AppDomain调用此方法会怎样?这将起作用,因为插件AppDomain中不涉及任何数据.所有数据均取自主机AppDomain,所有返回的数据仅在主机AppDomain中使用.代码来自不同(插件)域的事实并不重要,数据仅在堆栈上传递,该堆栈属于调用线程,并且所有涉及的堆都属于主机AppDomain.此用法不会尝试违反域边界.请注意,从未使用过"this",这将是一个问题.顺便说一下,也可以在主机AppDomain中通过插件接口实例化类.这样,您甚至可以使用"this"和实例方法.想法是仅将插件用作类和方法的源.类的实例(和所有其他数据)应始终在主机AppDomain端.

此示例还说明了线程不会感到域边界.当您尝试执行Dispatcher.InvokeDispatcher.BeginInvoke时,情况就不同了.这在整个Application Domain中将不起作用.为什么?因为Invoke不是一个电话,所以这个词在任何意义上都不是.这是纯数据交换.委托实例和用于调用的所有参数(包括调用类的实例)被放入队列以在UI线程中使用.但这就是所有数据,仅在一个AppDomain中有意义,而在其他任何cs中均不明智.无论如何,仅当要在一个AppDomain中运行带有数据的线程并且要处理在另一个AppDomain中运行的UI时,才可以使用IPC,但这不是不同应用程序域中线程之间的边界(这种边界不存在,甚至这种边界的概念都没有道理).这仍然是数据之间的保存边界.

问题2


TAB导航和所有其他方面将根据问题的答案(第1题答案中的解释)进行无缝定义.一旦插件创建了控件但这些控件属于主机AppDomain中的数据,它们的工作方式将与在相同的程序集和相同的AppDomain中创建的控件完全相同.

—SA


Plug-in implements operations on some UIElement set and adds some new controls. What happens if this method is called from a different AppDomain? It will work, because no data in the plug-in AppDomain are involved. All data are taken from the host AppDomain and all returned data is used only in the host AppDomain. The fact that the code is from the different (plug-in) domain does not matter, the data is passes only on stack, which belongs to the calling thread, and all heap involved belongs to the host AppDomain. This usage does not try to violate domain boundary. Note that "this" is never used, it would be a problem. By the way, instantiation of the classes by plug-in interface also can be done in the host AppDomain. In this way, you can even use "this" and instance method. The idea is to use the plug-in only as a source of classes and methods. Instances of the classes (and all other data) should always be in the host AppDomain side.

This example also illustrates that threads do not feel domain boundaries. When you try to do Dispatcher.Invoke or Dispatcher.BeginInvoke, this is different story. This will not work across Application Domain. Why? Because Invoke is not a call, not in any sense of this word. This is pure data exchange. A delegate instance and all parameters used for call (including instance of the calling class) are put into the queue to be used in the UI thread. But this is all data, only sensible in one AppDomain, not in any other. Anyway, you can only use IPC if you want run thread with data in one AppDomain and want to handle UI running in other AppDomain, but this is not a boundary between threads in different Application Domain (such boundary does not exist, and even the notion of such boundary makes no sense). This is still the save very boundary between data.

Question 2


The TAB navigation and all other aspects will work seamlessly by definition by the reasons explained in the answer to Q1. Once controls are created by plug-in but belong to data in the host AppDomain, they will work exactly in the same way as they were created in the same assembly and the same AppDomain.

—SA


这篇关于创建使用可重载插件的WPF应用程序...的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆