模块化程序。在主机中调用函数
Modular Program. Calling Functions In The Host
我已经使用
构建了一个模块化程序
http://www.codeproject.com/Articles/258681/Windows-Forms-Modular-App-using-MEF 作为基础,我有几个模块在工作。
这是一个 MDI Windows Forms 应用程序,我需要回调主机模块处理一些事情。
a) MDI 主机的位置信息 window
b) 写入主机中的状态栏window.
我已经设法让应用程序编译,但是当我调用主机函数时,它总是给我一个空异常
我看过Using MEF with C#, how do I call methods on the host, from the plugin?
这是我收到电话的地方
public Exec.Core.Interfaces.IHost Host;
但主机始终为空,因此我在尝试访问作为主机的 MDIForm 的成员时遇到异常。
即使我这样做 public Exec.Core.Interfaces.IHost 主机 {get;set;}
我是主持人。
NameSpace Exec
{
[Export(typeof(Exec.Core.Interfaces.IHost))]
// MDIForm is the host.
public partial class MDIForm : Form, Exec.Core.Interfaces.IHost
{
///other stuff not related to the problem
// defined in public interface IHost
public Point myLocation()
{
return this.Location; // need the window location
}
// defined in public interface IHost
public IHost GetHost()
{ // is this what GetHost Should Return? Not sure
return this;
}
// defined in public interface IHost
public void SendMessage(string message)
{
SetStatusBar(message); // print a message to MDIForm status bar
}
}
}
然后是IHosts.cs
namespace Exec.Core.Interfaces
{
public interface IHost
{
IHost GetHost();
void SendMessage(string message);
Point myLocation();
// MDIForm GetThis( ); /* this gives error. Can't resolve MDIForm
I don't know why and can't resolve.*/
}
}
这是我试图从主机获取内容的模块之一
namespace Exec.Modules.Tasks
{
[Export]
public partial class frmTasks : Form
{
[Import(typeof (Exec.Core.Interfaces.IHost))]
public Exec.Core.Interfaces.IHost Host;
// unfortunately Host == NULL at this point
private void SendMessage (string message)
{
try
{
Host.SendMessage(message); <Throws System.NullReferenceException
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private IHost WhichHost()
{
try
{ /// not really sure what will be returned here
return GetHost();<Throws System.NullReferenceException
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private Point Location()
{
try
{
return mylocation(); <Throws System.NullReferenceException
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
}
最后这就是我将所有对象放在一起的方式 ModuleHandler.cs
这几乎是从上面的代码项目中提取的,将一些方法调用分成两部分,所以我可以看出它为什么要死了。
namespace Exec.Core
{
[Export(typeof(IModuleHandler))]
public class ModuleHandler : IDisposable, IModuleHandler
{
[ImportMany(typeof(IModule), AllowRecomposition = true)]
// The ModuleList will be filled with the imported modules
public List<Lazy<IModule, IModuleAttribute>> ModuleList
{ get; set; }
[ImportMany(typeof(IMenu), AllowRecomposition = true)]
// The MenuList will be filled with the imported Menus
public List<Lazy<IMenu, IModuleAttribute>> MenuList { get; set; }
[Import(typeof(IHost))]
// The imported host form
public IHost Host { get; set; }
AggregateCatalog catalog = new AggregateCatalog();
public void InitializeModules()
{
// Create a new instance of ModuleList
ModuleList = new List<Lazy<IModule, IModuleAttribute>>();
// Create a new instance of MenuList
MenuList = new List<Lazy<IMenu, IModuleAttribute>>();
// Foreach path in the main app App.Config
foreach (var s in ConfigurationManager.AppSettings.AllKeys)
{
if (s.StartsWith("Path"))
{
// Create a new DirectoryCatalog with the path loaded from the App.Config
DirectoryCatalog cataloglist = new DirectoryCatalog(ConfigurationManager.AppSettings[s], "jobexe*.dll");
catalog.Catalogs.Add(cataloglist);
}
}
// Create a new catalog from the main app, to get the Host
catalog.Catalogs.Add( new AssemblyCatalog(System.Reflection.Assembly.GetCallingAssembly()));
// Create a new catalog from the ModularWinApp.Core
DirectoryCatalog catalogExecAssembly = new DirectoryCatalog( System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location ), "exe*.dll");
catalog.Catalogs.Add(catalogExecAssembly);
// Create the CompositionContainer
CompositionContainer cc = new CompositionContainer(catalog);
try
{
cc.ComposeParts(this);
}
catch (ReflectionTypeLoadException e)
{ MessageBox.Show(e.ToString()); }
catch (ChangeRejectedException e)
{ MessageBox.Show(e.ToString()); }
}
}
}
同样,模块独立工作但无法回调主机。想知道我做错了什么。
在此先感谢您的帮助
可能与问题有关的最后一件事。
这是启动程序的代码
public static ModuleHandler _modHandler = new ModuleHandler();
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//Initialize the modules. Now the modules will be loaded.
_modHandler.InitializeModules();
// this goes straight to class MDIForm() constructor
Application.Run(_modHandler.Host as Form);
}
科林
我有一个答案,但我认为它不合适。
关于我的问题,我编辑了它并添加了主程序,但我没有添加它的 class.
看起来像
namespace Exec
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
///
//Create a new instance of ModuleHandler. Only one must exist.
public static ModuleHandler _modHandler = new ModuleHandler();
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//Initialize the modules. Now the modules will be loaded.
_modHandler.InitializeModules();
Application.Run(_modHandler.Host as Form);
}
}
}
我正在调试,我发现在 _modHandler.InitializeModules();
IHost 主机已设置,其部分
public static ModuleHandler _modHandler = new ModuleHandler();
这里的一切都是静态的,但不可访问。所以我将 class 签名更改为 public 以使其成为全球性的(我知道脏话)
public static class Program
然后在
namespace Exec.Modules.Tasks
在 Load_Form 事件中我添加了一行来初始化主机。
public partial class frmTasks : Form
{
[Import(typeof (Exec.Core.Interfaces.IHost))]
public Exec.Core.Interfaces.IHost Host;
private void Load_Form(object sender, EventArgs e)
{
Host = Program._modHandler.Host.GetHost; << added this to initialize host
other stuff....
}
other stuff that now works
}
我不认为它应该是这样工作的。我认为我应该能够通过接口和模块填充它...
评论?
您手动实例化您的 ModuleHandler
,然后调用 InitializeModules
,在此处创建目录并将其传递给新的组合容器。然后,此容器用于通过以下行满足该特定 ModuleHandler
实例的所有导入:
cc.ComposeParts(this);
这会告诉 MEF 查找 Import
属性并使用 类 的实例填充装饰属性,这些实例装饰有相应的 Export
属性。
您缺少的是填充 frmTasks
对象的类似调用。因此,下面的Import
不满足,属性是null
:
[Import(typeof (Exec.Core.Interfaces.IHost))]
public Exec.Core.Interfaces.IHost Host;
你有几个选项,我会研究以下两个选项:
修改 IModule
接口,以便您可以显式地将 IHost
传递给模块。然后,在 InitializeModules
中,在调用 ComposeParts
之后,遍历组合模块,将它们传递给宿主实例。这说明通过 IModule
界面设置 Host
属性。您还可以通过将其放入 IModule
接口并为每个模块实例调用 ComposeParts
来坚持导入的 MEF 属性。
公开容器 through a ServiceLocator 并从模块中获取 IModuleHandler
实例以访问 Host
属性.
我已经使用
构建了一个模块化程序http://www.codeproject.com/Articles/258681/Windows-Forms-Modular-App-using-MEF 作为基础,我有几个模块在工作。
这是一个 MDI Windows Forms 应用程序,我需要回调主机模块处理一些事情。
a) MDI 主机的位置信息 window
b) 写入主机中的状态栏window.
我已经设法让应用程序编译,但是当我调用主机函数时,它总是给我一个空异常
我看过Using MEF with C#, how do I call methods on the host, from the plugin?
这是我收到电话的地方
public Exec.Core.Interfaces.IHost Host;
但主机始终为空,因此我在尝试访问作为主机的 MDIForm 的成员时遇到异常。
即使我这样做 public Exec.Core.Interfaces.IHost 主机 {get;set;}
我是主持人。
NameSpace Exec
{
[Export(typeof(Exec.Core.Interfaces.IHost))]
// MDIForm is the host.
public partial class MDIForm : Form, Exec.Core.Interfaces.IHost
{
///other stuff not related to the problem
// defined in public interface IHost
public Point myLocation()
{
return this.Location; // need the window location
}
// defined in public interface IHost
public IHost GetHost()
{ // is this what GetHost Should Return? Not sure
return this;
}
// defined in public interface IHost
public void SendMessage(string message)
{
SetStatusBar(message); // print a message to MDIForm status bar
}
}
}
然后是IHosts.cs
namespace Exec.Core.Interfaces
{
public interface IHost
{
IHost GetHost();
void SendMessage(string message);
Point myLocation();
// MDIForm GetThis( ); /* this gives error. Can't resolve MDIForm
I don't know why and can't resolve.*/
}
}
这是我试图从主机获取内容的模块之一
namespace Exec.Modules.Tasks
{
[Export]
public partial class frmTasks : Form
{
[Import(typeof (Exec.Core.Interfaces.IHost))]
public Exec.Core.Interfaces.IHost Host;
// unfortunately Host == NULL at this point
private void SendMessage (string message)
{
try
{
Host.SendMessage(message); <Throws System.NullReferenceException
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private IHost WhichHost()
{
try
{ /// not really sure what will be returned here
return GetHost();<Throws System.NullReferenceException
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private Point Location()
{
try
{
return mylocation(); <Throws System.NullReferenceException
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
}
最后这就是我将所有对象放在一起的方式 ModuleHandler.cs 这几乎是从上面的代码项目中提取的,将一些方法调用分成两部分,所以我可以看出它为什么要死了。
namespace Exec.Core
{
[Export(typeof(IModuleHandler))]
public class ModuleHandler : IDisposable, IModuleHandler
{
[ImportMany(typeof(IModule), AllowRecomposition = true)]
// The ModuleList will be filled with the imported modules
public List<Lazy<IModule, IModuleAttribute>> ModuleList
{ get; set; }
[ImportMany(typeof(IMenu), AllowRecomposition = true)]
// The MenuList will be filled with the imported Menus
public List<Lazy<IMenu, IModuleAttribute>> MenuList { get; set; }
[Import(typeof(IHost))]
// The imported host form
public IHost Host { get; set; }
AggregateCatalog catalog = new AggregateCatalog();
public void InitializeModules()
{
// Create a new instance of ModuleList
ModuleList = new List<Lazy<IModule, IModuleAttribute>>();
// Create a new instance of MenuList
MenuList = new List<Lazy<IMenu, IModuleAttribute>>();
// Foreach path in the main app App.Config
foreach (var s in ConfigurationManager.AppSettings.AllKeys)
{
if (s.StartsWith("Path"))
{
// Create a new DirectoryCatalog with the path loaded from the App.Config
DirectoryCatalog cataloglist = new DirectoryCatalog(ConfigurationManager.AppSettings[s], "jobexe*.dll");
catalog.Catalogs.Add(cataloglist);
}
}
// Create a new catalog from the main app, to get the Host
catalog.Catalogs.Add( new AssemblyCatalog(System.Reflection.Assembly.GetCallingAssembly()));
// Create a new catalog from the ModularWinApp.Core
DirectoryCatalog catalogExecAssembly = new DirectoryCatalog( System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location ), "exe*.dll");
catalog.Catalogs.Add(catalogExecAssembly);
// Create the CompositionContainer
CompositionContainer cc = new CompositionContainer(catalog);
try
{
cc.ComposeParts(this);
}
catch (ReflectionTypeLoadException e)
{ MessageBox.Show(e.ToString()); }
catch (ChangeRejectedException e)
{ MessageBox.Show(e.ToString()); }
}
}
}
同样,模块独立工作但无法回调主机。想知道我做错了什么。
在此先感谢您的帮助
可能与问题有关的最后一件事。
这是启动程序的代码
public static ModuleHandler _modHandler = new ModuleHandler();
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//Initialize the modules. Now the modules will be loaded.
_modHandler.InitializeModules();
// this goes straight to class MDIForm() constructor
Application.Run(_modHandler.Host as Form);
}
科林
我有一个答案,但我认为它不合适。 关于我的问题,我编辑了它并添加了主程序,但我没有添加它的 class.
看起来像
namespace Exec
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
///
//Create a new instance of ModuleHandler. Only one must exist.
public static ModuleHandler _modHandler = new ModuleHandler();
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//Initialize the modules. Now the modules will be loaded.
_modHandler.InitializeModules();
Application.Run(_modHandler.Host as Form);
}
}
}
我正在调试,我发现在 _modHandler.InitializeModules();
IHost 主机已设置,其部分
public static ModuleHandler _modHandler = new ModuleHandler();
这里的一切都是静态的,但不可访问。所以我将 class 签名更改为 public 以使其成为全球性的(我知道脏话)
public static class Program
然后在
namespace Exec.Modules.Tasks
在 Load_Form 事件中我添加了一行来初始化主机。
public partial class frmTasks : Form
{
[Import(typeof (Exec.Core.Interfaces.IHost))]
public Exec.Core.Interfaces.IHost Host;
private void Load_Form(object sender, EventArgs e)
{
Host = Program._modHandler.Host.GetHost; << added this to initialize host
other stuff....
}
other stuff that now works
}
我不认为它应该是这样工作的。我认为我应该能够通过接口和模块填充它...
评论?
您手动实例化您的 ModuleHandler
,然后调用 InitializeModules
,在此处创建目录并将其传递给新的组合容器。然后,此容器用于通过以下行满足该特定 ModuleHandler
实例的所有导入:
cc.ComposeParts(this);
这会告诉 MEF 查找 Import
属性并使用 类 的实例填充装饰属性,这些实例装饰有相应的 Export
属性。
您缺少的是填充 frmTasks
对象的类似调用。因此,下面的Import
不满足,属性是null
:
[Import(typeof (Exec.Core.Interfaces.IHost))]
public Exec.Core.Interfaces.IHost Host;
你有几个选项,我会研究以下两个选项:
修改
IModule
接口,以便您可以显式地将IHost
传递给模块。然后,在InitializeModules
中,在调用ComposeParts
之后,遍历组合模块,将它们传递给宿主实例。这说明通过IModule
界面设置Host
属性。您还可以通过将其放入IModule
接口并为每个模块实例调用ComposeParts
来坚持导入的 MEF 属性。公开容器 through a ServiceLocator 并从模块中获取
IModuleHandler
实例以访问Host
属性.