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

时间:2016-01-12 05:43:00

标签: c# mef

我使用

构建了一个模块化程序

http://www.codeproject.com/Articles/258681/Windows-Forms-Modular-App-using-MEF作为基础,我有几个模块在工作。

这是一个MDI Windows Forms应用程序,我需要回调一下主机模块。

a)MDI主机窗口的位置信息

b)写入主机窗口中的状态栏。

我设法让应用程序编译但是当我调用主机函数时它总是给我一个空的异常

我确实看过Using MEF with C#, how do I call methods on the host, from the plugin?

这是我得到我的行

public Exec.Core.Interfaces.IHost Host;

但是host始终为null,因此我尝试访问作为主机的MDIForm的成员时遇到异常。

即使我公开Exec.Core.Interfaces.IHost Host {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);

}

科林

2 个答案:

答案 0 :(得分:0)

我有一个答案,我只是不认为它是对的。 在我的问题上,我编辑了它并添加了主程序,但我没有添加它的类。

看起来像是

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 Host已设置,其部分

public static ModuleHandler _modHandler = new ModuleHandler();

此处的所有内容都是静态的,但无法访问。所以我将类签名更改为public以使其全局化(我知道的脏词)

public static class Program

然后在

namespace Exec.Modules.Tasks
在Load_Form事件中,我添加了一行来初始化Host。

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
}

我不认为这是它应该如何运作的。我认为我应该能够通过接口和模块来填充它......

评论

答案 1 :(得分:0)

手动实例化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之后,迭代组合模块,将它们传递给主机实例。这说明了通过Host界面设置IModule属性。您还可以将MEF导入的属性放在IModule界面并为每个模块实例调用ComposeParts

  • 公开容器through a ServiceLocator并从模块中获取IModuleHandler实例以访问Host属性。