Wtyczki DLL - wywoływanie procedury z wtyczki

0

Po wielu próbach z pozytywnym skutkiem udało mi się zrealizować taki sposób implementacji obsługi wtyczek, poniżej przykład testowy.

Program główny:

    public partial class Form1 : Form
    {

        public Form1()
        {
            InitializeComponent();
        }

        string PlugFile = "";


        Assembly As;
        Object Ob;
        MethodInfo Mstart;
        MethodInfo Mrec;
        MethodInfo Msnd;

        // Uruchom
        private void button1_Click(object sender, EventArgs e)
        {
            As = Assembly.LoadFile(PlugFile);
            Type[] TT = As.GetTypes();

            foreach (Type Tw in TT)
            {
                // Kanal - nazwa interfejsu
                if (Tw.ToString().IndexOf(".TestDll") > (-1))
                {
                    Ob = Activator.CreateInstance(Tw);  
                    
                    Mstart = Tw.GetMethod("Wystartuj");
                    Mrec = Tw.GetMethod("Pobierz");
                    Msnd = Tw.GetMethod("Wyswietl");
                }
            }
            Mstart.Invoke(Ob, null);
        }

        // Wyslij
        private void button2_Click(object sender, EventArgs e)
        {
            Object[] Params = new Object[1];
            Params[0] = textBox1.Text;
            Msnd.Invoke(Ob, Params);
        }

        // Pobierz
        private void button3_Click(object sender, EventArgs e)
        {
            label1.Text = (string)(Mrec.Invoke(Ob, null));
        }

        private void button4_Click(object sender, EventArgs e)
        {
            OpenFileDialog Op = new OpenFileDialog();
            if (Op.ShowDialog() == DialogResult.OK)
            {
                
            }
            PlugFile = Op.FileName;
        }
    }

Wtyczka:

    public class TestDll
    {
        Form1 Frm;

        public void Wyswietl(string S)
        {
            Frm.label1.Text = S;
        }

        public string Pobierz()
        {
            return Frm.textBox1.Text;
        }

        public void Wystartuj()
        {
            Frm = new Form1();
            Frm.Visible = true;
        }
    }

Wszysko ładnie gra. Z programu głównego mogę wywoływać metody zawarte we wtyczce, co umożliwia wprowadzanie danych do wtyczki, oraz wyciąganie tych danych na żądanie programu głównego.

Natomiast, czy da się zrobić metodę zawartą w programie głównym wywoływaną na żądanie wtyczki?

Na przykład w programie głównym jest metoda:

public void TestPlugin()
{
    MessageBox.Show("Dziala");
}

A na formie wtyczki jest przycisk "Test", którego kliknięcie spowoduje uruchomienie powyższej metody z programu głównego.

1

Wtyczka musiałaby importować program główny co jest trochę bez sensu (a może się mylę?). Zrób to za pomocą zdarzeń.

0
MSM napisał(a)

Wtyczka musiałaby importować program główny co jest trochę bez sensu (a może się mylę?). Zrób to za pomocą zdarzeń.

A z kolei, jak w programie głównym jest jakaś zmienna typu string, int itd., to jak do niej dojść z poziomu wtyczki?

0

Musisz w którymś momencie przekazać do dll referencje na jakis obiekt już z Twojego programu, najprościej.

Tak wygladała by implementacja po stronie programu

    interface IPluginTester
    {
        void Test(object dll_id);
    }
    class JakasKlasaWProgramie : IPluginTester
    {
        #region IPluginTester Members

        public void Test(object dll_id)
        {
            //...
        }

        #endregion
    }

//Po stronie DLL, na plugin_testerze wywolujesz jakies tam metody.

    class TestDll
    {
        //Fields
        object dll_id = ???;
        IPluginTester plugin_tester = null;

        //Methods
        public void Initialize(IPluginTester plugin_tester)
        {
            this.plugin_tester = plugin_tester;
            this.plugin_tester.Test(dll_id);
        }
    }
0

Już coś, ale chyba nie do końca dobrze i mem problem, który uniemożliwia implementację i zrobienie próby.

Program

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Reflection;

namespace MainPrg
{
    public partial class Form1 : Form, IPluginTester
    {

        public Form1()
        {
            InitializeComponent();
        }

        string PlugFile = "E:\\Csharp\\DLLtest\\DLLtest\\DLLtest\\bin\\Debug\\DLLtest.dll";


        Assembly As;
        Object Ob;
        MethodInfo Mstart;
        MethodInfo Mrec;
        MethodInfo Msnd;
        

        // Uruchom
        private void button1_Click(object sender, EventArgs e)
        {
            As = Assembly.LoadFile(PlugFile);
            Type[] TT = As.GetTypes();

            foreach (Type Tw in TT)
            {
                if (Tw.ToString().IndexOf(".TestDll") > (-1))
                {

                    Ob = Activator.CreateInstance(Tw);

                    Mstart = Tw.GetMethod("Wystartuj");
                    Mrec = Tw.GetMethod("Pobierz");
                    Msnd = Tw.GetMethod("Wyswietl");
                }
            }
            Mstart.Invoke(Ob, null);
        }
        
        public int PluginVar = 0;

        // Wyslij
        private void button2_Click(object sender, EventArgs e)
        {
            Object[] Params = new Object[1];
            Params[0] = textBox1.Text;
            Msnd.Invoke(Ob, Params);
        }

        // Pobierz
        private void button3_Click(object sender, EventArgs e)
        {
            label1.Text = (string)(Mrec.Invoke(Ob, null));
        }

        private void button4_Click(object sender, EventArgs e)
        {
            OpenFileDialog Op = new OpenFileDialog();
            if (Op.ShowDialog() == DialogResult.OK)
            {
                
            }
            PlugFile = Op.FileName;
        }

        // Metoda testowa
        public void TestPlugin(object dll_id)
        {
            MessageBox.Show("Metoda wywolana z wtyczki. " + (string)dll_id);
        }

    }

    interface IPluginTester
    {
        void TestPlugin(object dll_id);
    }
}

Wtyczka

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Reflection;

namespace DLLtest
{
    public class TestDll
    {
        Form1 Frm;


        Object dll_id = "Czesc";
        IPluginTester plugin_tester = null;

        public void Initialize(IPluginTester plugin_tester)
        {
            this.plugin_tester = plugin_tester;
            this.plugin_tester.Test(dll_id);
        }

        public void Wyswietl(string S)
        {
            Frm.label1.Text = S;
        }

        public string Pobierz()
        {
            return Frm.textBox1.Text;
        }

        public void Wystartuj()
        {
            Frm = new Form1();
            Frm.Visible = true;
        }
    }
}

Przy próbie skompilowania wtyczki program pokazuje, że nie jest zadeklarowane słowo "IPluginTester", jednak mnie to nie dziwi, bo to słowo nigdzie nie jest zadeklarowane w projekcie wtyczki, a jest użyte.

Jeżeli dokleję interfejs taki sam, jak w programie głównym, to pojawia się taki błąd:
Inconsistent accessibility parameter type "DLLtest.IPluginTester" is less accessible than method "DLLtest.TestDLL.initialize (DLLtest.IPluginTester)"

Najwyraźniej coś przeoczyłem. Co jeszcze muszę poprawić?

0

No interfejs, rzecz jasna, musi być współdzielony przez program i dllki

0
dark_astray napisał(a)

No interfejs, rzecz jasna, musi być współdzielony przez program i dllki

Ja to pierwszy raz coś takiego robię. Tylko, że prog i DLL to dwa osobne projekty, bo chce przewidzieć tworzenie kolejnych wtyczej jako następne projekty, również, jeżeli się da, bez dostępu do kodu programu (przy założeniu, że kazdy DLL będzie mieć ten sam zestaw metod/zmiennych do komunikacji z programem głównym, a cała reszta to już jest ta wtyczka). W takim razie jak to zrobić, żeby współdzielić jeden interfejs, lub "powiedzieć" kompilatorowi (VS2008), że ma właśnie skorzystać z tego interfejsu przy uruchamianiu wtyczki.

To, co przedstawiam powyżej to próbne projekty do prób, które później zamierzam przenieść do tego właściwego.

0

Wymysliłem coś takiego na szybko.
W oddzielnej Dll masz przykładowo takie interfejsy jak niże, z tej dll korzysta i program i pluginy

    public interface IApplicationContext
    {
        void ShowMessage(string message);
    }
    public interface IPlugin
    {
        void AddContext(IApplicationContext app_context);
        void RemoveContext(IApplicationContext app_context);
    }

dalej przykładowy plugin mogłby wygladać tak

    class ABPlugin:IPlugin
    {
        //Fields
        string plugin_id = "AB Plugin";
        List<IApplicationContext> app_contexts = new List<IApplicationContext>();

        void NotifyContexts()
        {
            foreach (IApplicationContext context in this.app_contexts)
            {
                context.ShowMessage(this.plugin_id);
            }
        }

        #region IPlugin Members

        public void AddContext(IApplicationContext app_context)
        {
            this.app_contexts.Add(app_context);
        }

        public void RemoveContext(IApplicationContext app_context)
        {
            this.app_contexts.Remove(app_context);
        }

        #endregion
    }

No i jakaś przykładowa instancja programu

    interface IExtensible
    {
        void Attach(IPlugin plugin);
        void Detach(IPlugin plugin);
    }

    class ApplicationInstance:IApplicationContext,IExtensible
    {
        //Fields
        List<IPlugin> plugins = new List<IPlugin>();
        
                
        #region IApplicationContext Members

        public void ShowMessage(string message)
        {
            Console.WriteLine(message);
        }

        #endregion

        #region IExtensible Members

        public void Attach(IPlugin plugin)
        {
            plugin.AddContext(this);
            this.plugins.Add(plugin);
        }

        public void Detach(IPlugin plugin)
        {
            plugin.RemoveContext(this);
            this.plugins.Remove(plugin);
        }

        #endregion
    }

pluginy do Instancji programu dodajesz poprzez Attach(IPlugin)
Może się przyda
Pozdrawiam.

0

Spróbowałem i mam ten sam problem z niezadeklarowanymi słowami, tylko, że w programie głównym. Wydaje mi się, że kopiowanie interfejsu z pluginu mija się z celem, a wywołanie Attach przez Invoke to też nie da się skompilować.

Wygląda na to, że rzeczywiście coś kręcę.

Tutaj cały projekt próbny. Czy możesz go przejrzeć i wskazać gdzie popełniłem błąd? Może nie jest on w kodzie, tylko gdzieś indziej? http://www.sendspace.com/file/hiabdc
Program główny:
Wybierz - wybór pliku DLL
Uruchom - uruchamianie wtyczki (pojawia się drugie okienko)
Wyślij - tekst wpisany w pole tekstowe pojawia się w okienku wtyczki
Odbierz - tekst z pola tekstowego wtyczki pojawia się w oknie głównym

Wtyczka: Przycisk button1 ma uruchamiać procedurę zawartą w programie głównym.

0
dark_astray napisał(a)

Wymysliłem coś takiego na szybko.
W oddzielnej Dll masz przykładowo takie interfejsy jak niżej, z tej dll korzysta i program i pluginy

    public interface IApplicationContext
    {
        void ShowMessage(string message);
    }
    public interface IPlugin
    {
        void AddContext(IApplicationContext app_context);
        void RemoveContext(IApplicationContext app_context);
    }
0
dark_astray napisał(a)
dark_astray napisał(a)

Wymysliłem coś takiego na szybko.
W oddzielnej Dll masz przykładowo takie interfejsy jak niżej, z tej dll korzysta i program i pluginy

    public interface IApplicationContext
    {
        void ShowMessage(string message);
    }
    public interface IPlugin
    {
        void AddContext(IApplicationContext app_context);
        void RemoveContext(IApplicationContext app_context);
    }

Zaraz. Czy to znaczy, ze chodzi o utworzenie trzeciego projektu, którym jest DLL i ten DLL wczyta zarówno program, jak i wtyczka?

0

Tak. Kopiowanie interfejsu nic nie daje, bo framework rozroznia je jako osobne byty, glownie dlatego, ze oprocz nazwy interfejsu sprawdza tez czy pochodzi z tej samej dllki. Identyczne interfejsy w 2 roznych dllkach, to z punktu widzenia frameworka 2 rozne interfejsy.

Powinienes dojsc do takiej sytuacji, ze masz 3 dllki:

  • core.dll - interfejsy
  • plugin.dll - wtyczka
  • app.exe - aplikacja

wtyczka i aplikacja implementuja interfejsy z tego samego miejsca, wiec niejako wiedza o sobie nawzajem - bo znaja ta sama definicje interfejsu. Nie potrzebuja znac implementacji - czyli nie musza referowac do siebie wzajemnie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1