c# Plugin (reflection)

2019-05-31 18:58发布

问题:

I'm having trouble with creating a plugin solution with reflection. When I click on a menuItem I would like to load and show another program in my window. I figured I would need 3 projects.

  1. Client program that wants to load another program when clicked on menuItem
  2. Program that I want to load
  3. Plugin class

Code client program

private void nQueensToolStripMenuItem_Click(object sender, EventArgs e)
    {

        // Create an assembly object to load our classes
        string path = Application.StartupPath + "\\NQueens.dll";
        Assembly ass = Assembly.LoadFile(path);

        Type objType = ass.GetType("NQueens.NQueen");

        // Create an instace of NQueens.NQueen
        var instance = Activator.CreateInstance(objType);

        // public static bool berekenQueens()
        objType.InvokeMember("berekenQueens",
            BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Static,
            null, instance, null);

        // private static bool bordValidatie
        objType.InvokeMember("bordValidatie",
            BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Static,
            null, instance, null);
    }

Class

[AttributeUsage(AttributeTargets.Class)]
public class myPluginAttribute : Attribute
{
    private bool _bIsPlugin;
    public Boolean IsPlugin
    {
        get { return _bIsPlugin; }
        set { _bIsPlugin = value; }
    }
    private string _sDescription;
    public string Description
    {
        get { return _sDescription; }
        set { _sDescription = value; }
    }

    public myPluginAttribute(Boolean b, String desc)
    {
        IsPlugin = b;
        Description = desc;
    }
}

NQueens (program i want to load when clicked on menuitem) NQueens.cs

public class NQueen
{
   public static bool berekenQueens(int Row, int N, bool[,] bord)
   {
       if (Row >= N) return true; 

       for (int Col = 0; Col < N; Col++)
       {
           //Q toevoegen
           bord[Row, Col] = true;

           //Q + Q volgende Row  controleren
           if (bordValidatie(Row, Col, bord, N) && berekenQueens(Row + 1, N, bord))
           {
               return true;
           }

           //Q verwijderen indien niet door controle
           bord[Row, Col] = false;

       }
       return false;
   }


   private static bool bordValidatie(int currentRow, int currentCol, bool[,] currentBord, int N)
   {
       int colstep = 1;
       for (int i = currentRow - 1; i >= 0; i--)
       {
           //rechte lijn 
           if (currentBord[i, currentCol])
               return false;

           //linker diagonaal
           if (currentCol - colstep >= 0)
           {
               if (currentBord[i, currentCol - colstep])
                   return false;
           }
           //rechter diagonaal
           if (currentCol + colstep < N)
           {
               if (currentBord[i, currentCol + colstep])
                   return false;
           }
           colstep += 1;
       }
       return true;
   }


}

MainWindow (nqueens)

public partial class MainWindow : Window
{
    public int iN { get { return Convert.ToInt32(txtN.Text); } set { txtN.Text = "" + value; } }
    private bool[,] spelbord;

    public MainWindow()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        spelbord = new bool[iN, iN];
        NQueen.berekenQueens(0, iN, spelbord);

        visualise(iN, spelbord);
    }

    private void visualise(int N, bool[,] bord)
    {
        gridTekenen();

        for (int row = 0; row < N; row++)
        {
            for (int col = 0; col < N; col++)
            {
                Rectangle rect = new Rectangle();
                rect.Stretch = Stretch.Fill;

                TextBlock txtB = new TextBlock();

                if (spelbord[row, col])
                {
                    SolidColorBrush mySolidColorBrush = new SolidColorBrush();
                    mySolidColorBrush.Color = Colors.LightGreen;
                    rect.Fill = mySolidColorBrush;

                    txtB.Text = "Q";
                }

                rect.SetValue(Grid.ColumnProperty, col);
                rect.SetValue(Grid.RowProperty, row);

                txtB.SetValue(Grid.ColumnProperty, col);
                txtB.SetValue(Grid.RowProperty, row);

                gridPaneel.Children.Add(rect);
                gridPaneel.Children.Add(txtB);
            }
        }
    }

    private void gridTekenen()
    {
        gridPaneel.ShowGridLines = true;
        int grooteGrid = int.Parse(txtN.Text);

        RowDefinition rowDef;
        ColumnDefinition colDef;


        for (int i = 0; i < grooteGrid; i++)
        {
            rowDef = new RowDefinition();

            GridLengthConverter myGridLengthConverter = new GridLengthConverter();
            GridLength gl1 = (GridLength)myGridLengthConverter.ConvertFromString(150 + "*");
            rowDef.Height = gl1;

            colDef = new ColumnDefinition();
            colDef.Width = gl1;

            gridPaneel.RowDefinitions.Add(rowDef);
            gridPaneel.ColumnDefinitions.Add(colDef);
        }


    }
}

I'm stuck trying to get this thing working. How can I make my program to show the NQueens program? How does my client program know the methods from NQueen and use them?

EDIT: I made some changes in the nQueensToolStripMenuItem_Click method. Is this correct now? How can I show the NQueens method in my window now?

回答1:

As far as I know, you will need a common Interface, which helps communicating with the "Extensions". You will then have three projects:

  • Your Core Application
  • A Library just containing the Interface (i.e. IPlugin)
  • Your Extensions.

Have a look at codeproject to get a better idea about the architecture of plugins.



回答2:

A much simpler pattern is the classic "Factory Pattern" with interfaces. I've done this before to create plug-ins and it's really straightforward:

1) Create an interface containing all the properties and methods you would ever use to "talk" to the plug-in. Define this in a base class/assembly that will (hardly) ever change.

2) Create a frame UI application which perhaps has one stock implementation of the plug-in interface, perhaps the default menus, so you can easily debug and get your interface right.

3) Now to the dynamic loading... Create a configuration string listing a number of files which should be loaded. Good to allow this to be configurable rather than hard-coding paths, also for security never just load assemblies in a path, you don't know who could have put them there via a back door in file permissions.

4) On start-up use the assembly load from methods to go through each file and once loaded use reflection to query the interfaces, looking for your plugin interface. Store a collection of loaded interfaces/plug-ins and use them as you wish.

Obviously as you have a UI you will have a method to create the child menu items and windows and set their parent to your frame applications menu or client area appropriately. Then you will have some events defined on the interface to deal with events like items being clicked. Makes sense to also define a standard "tool" or command interface, so you can deal with menu items and toolbar items the same way.