模块化程序。在主机中调用函数

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 属性.