Jak rozšířit aplikaci o zásuvné moduly (Plug-in)

Ondřej Linhart       24. 3. 2008       VB.NET, Architektura       9279 zobrazení

Vzhledem k zájmu v oblasti rozšíření aplikace pomocí zásuvných modulů (dynamické volání knihoven) jsem se rozhodl napsat tento krátký článek. Měl by vám pomoci pochopit základní princip a ukázat jednoduchý příklad v praxi.

Úvod

Spousta moderních aplikací ať už jsou to prohlížeče obrázků, multimediální přehrávače, nebo editory podporuje tzv. zásuvné moduly, známé také jako Plug-in nebo Add-on. Tyto doplňky jsou v podstatě miniaplikace, které nějakým způsobem rozšiřují funkčnost hostitelské aplikace (většinou je to podpora dalších formátů dat, nebo grafické či zvukové filtry nebo efekty).

Trocha teorie

Hostitelská aplikace musí nějakým způsobem jednotně definovat komunikaci se všemi zásuvnými moduly a toho se dá snadno dosáhnout pomocí implementace rozhraní. V praxi je to možné realizovat tak, že každý zásuvný modul bude reprezentovat jedna třída implementující právě toto rozhraní a konkrétní implementace bude zajišťovat požadovanou funkčnost zásuvného modulu. Zásuvný modul (nebo více zásuvných modulů) je nejlepší umístit do knihovny DLL, která bude mít odkaz na další knihovnu DLL ve které bude specifikováno rozhraní. Tato knihovna s rozhraním bude potom společná jak pro hostitelskou aplikaci, tak pro zásuvné moduly (bude vlastně představovat jednoduché SDK, které budete distribuovat společně s dokumentací všem zájemcům o tvorbu zásuvných modulů pro vaší aplikaci). Možná vás napadlo proč rozhraní nenapsat přímo do hostitelské aplikace a na to je jednoduchá odpověď: Třetím stranám nemusíte distribuovat celou vaší aplikaci (což ani ve většině případů není žádoucí) ale pouze DLL s definicí rozhraní. Jednotlivé zásuvné moduly se potom mohou na toto rozhraní odkazovat přímo (přidáním reference).

Konkrétní příklad

Ukázkové řešení které jsem se snažil napsat co nejjednodušeji a nejsrozumitelněji včetně komentářů klíčových částí kódu představuje hostitelskou konzolovou aplikaci, která sama o sobě nemá žádnou funkčnost. Funkčnost této aplikace je zajištěna právě pomocí čtyř ukázkových zásuvných modulů představujících základní matematické operace. Knihovna BasicMath obsahuje dva zásuvné moduly - Sčítání a Odčítání, knihovna AdvancedMath potom Násobení a Dělení. Tím jsem chtěl ukázat, že v jedné knihovně může být jeden nebo více zásuvných modulů a zároveň může být více knihoven se zásuvnými moduly (dvě různé firmy vyvinou knihovny s různým počtem zásuvných modulů pro vaší aplikaci). Hostitelská aplikace nejprve prohledá svoji složku na výskyt DLL knihoven a poté se pokusí ze všech nalezených knihoven vytvořit instance všech v nich existujících zásuvných modulů které odpovídají definovanému rozhraní. Poté jsou vytvořeny náhodné vstupní hodnoty a tyto předány všem zásuvným modulům v seznamu pro účely testování. Pokud pochopíte základy, neměl by být pro vás problém napsat si vlastní zásuvný modul například pro umocňování.

Stáhnout ukázkovou aplikaci

Po spuštění a přeložení ukázkového řešení je třeba zkopírovat DLL knihovny AdvancedMath.dll a BasicMath.dll do složky s hostitelskou aplikací PluginsHost.exe. V rámci testování můžete samozřejmě tyto knihovny smazat a hostitelská aplikace se vám přesto spustí, ovšem bez jakékoliv funkčnosti.

 

hodnocení článku

5 bodů / 5 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Mohlo by vás také zajímat

Programování Windows Services

Services (služby) jsou jedním ze základních stavebních kamenů programování pro systémy Windows. V článku se budu věnovat jak obecným principům a doporučením, tak konkrétním implementačním postupům.

Windows Presentation Foundation (WPF) - díl 7.: Grid

Grid je jedna z nejdůležitější a nejpoužívanějších pozicovacích komponent ve WPF. Ulehčuje návrh formulářů a své uplatnění nachází v řadě scénářů.

představení nového seriálu o ASP .net MVc - from zero to hero

 

 

Nový příspěvek

 

Diskuse: Jak rozšířit aplikaci o zásuvné moduly (Plug-in)

Perfektní, názorné a poučné! Díky

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Moc mne to zaujalo ale narazil jsem na problém když jsem to chtěl zkusit implantovat do Form_1...ale nevim proč

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Když mi napíšete k jaké chybě u vás dochází, rád vám poradím.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

yfoycsl

nahlásit spamnahlásit spam 0 odpovědětodpovědět

qapshtdw <a href="http://voolyk.com/ ">rfcwtkvr</a>

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Jak rozšířit aplikaci o zásuvné moduly (Plug-in)

Jako reakci na některé dotazy k výbornému článku pana Linharta jsem se snažil jeho kód aplikovat do trošičku praktické košilky, aby bylo zřejmé, jak se dá s technologií zásuvných modulů pracovat. Neberte to, prosím, jako odborný výklad, spíše jako vyjádření, jak jsem lekci pana Linharta pochopil já (s plug-iny jsem pracoval poprvé).

Omlouvám se také panu Hercegovi a Jechovi, nevím jaká mají v diskusních příspěvcích omezení takže doufám, že ta přemíra textu neshodí místní server (ale bohužel jsem nevěděl, kam jinam bych to mohl hodit)

Takže "malé" praktické cvičení:

Pokusíme se sestavit skutečně funkční kalkulačku:

1) Spustíme VB -> Nový projekt

2) Zvolíme Class Library (jako první si vytvoříme potřebná rozhranní), do Name dáme třeba PlugInterf (název

naší knihovny s rozhraními), Do solution Name cokoliv, třeba Kalkulator.

3) V SolutionExploreru přejmenujte Class1.vb třeba na MathInterface.vb

4) v kódu smažte Public Class - end Class a nahraďte kódem

Public Interface IMathDual

    ReadOnly Property name() As String

    ReadOnly Property znacka() As String

    Function Operace(ByVal x As Single, ByVal y As Single, ByRef vysledek As Double) As String

End Interface

Se stejnou filosofií si pod tento interface (do stejného souboru) doplníme ještě rozhranní druhé, tentokrát pro

matematické operace, které pro svůj chod potřebují jako vstup pouze jeden operand:

Public Interface IMathPrim

    ReadOnly Property name() As String

    ReadOnly Property znacka() As String

    Function Operace(ByVal x As Single, ByRef vysledek As Double) As String

End Interface

(kód by bylo vhodné okomentovat, zvláště budete-li jej distribuovat dále, pro šetření místem a časem

to nechám bez komentáře, funkčnost bude jasná z plug-inů (alespoň je vidět, jak je to jednoduché!:-)

5) Sestavte knihovnu (menu->Build->Build PlugInterf)

Tím se nám vytvořila knihovna (v podadresáři Pluginterf\bin\debug) se jménem PlugInterf.dll, která obsahuje definici našich rozhranní.

6) Nyní si vytvoříme jeden zásuvný modul:

7) Vytvořte si nový projekt: v solutionExploreru klepněte pravým tlačítkem myši a vyberte Add-> New project

8) typ projektu zvolte opět Class Library a do názvu dejte třeba Matika01

9) opět si přejmenujte v solutionExploreru Class1.vb na Matika1.vb

10) V projektu Matika01 klepněte (v solution Exploreru) 2x na My Project, čímž se Vám otevře karta nastavení projektu, kde si na záložce References přidejte referenci na právě vytvořenou knihovnu (add->Browse->na disku vyhledejte PlugInterf.dll)

11) Přepněte se do okna s kódem Matika1.vb. Přejmenujte Class Matika1 na

Public Class Scitani

12) abychom se mohli odvolávat přímo na jednotlivá definovaná rozhranním, napište nad deklaraci Public Class Scitani ještě příkaz

Imports PlugInterf

13) Jako první řádek do těla naší třídy Scitani zapište, že tato třída je vlastně konkrétní implementací námi definovaného rozhranní:

 Implements IMathDual

14) Okamžitě jsme si všimli, že VS za nás udělalo podstatnou část práce - dle toho, co jsme definovali v rozhranní nám Visual Studio sestavilo kompletní kostru naší třídy (jak jsme již dříve zmínili, v rozhranní jsme definovali, co naše třída musí obsahovat, a tady je první aplikování této informace).

Takže kostru již máme, nyní jenom doplníme, jak přesně jednotlivé vlastnosti a metody budou v naší třídou implementovány:

15) doplníme kód naší třídy třeba takto:

Imports PlugInterf

Public Class Scitani
    Implements IMathDual

    Public ReadOnly Property name() As String Implements PlugInterf.IMathDual.name
        Get
            Return "Sčítání"
        End Get
    End Property

    Public Function Operace(ByVal x As Single, ByVal y As Single, ByRef vysledek As Double) As String Implements 

PlugInterf.IMathDual.Operace
        vysledek = x + y
        Return ""
    End Function

    Public ReadOnly Property znacka() As String Implements PlugInterf.IMathDual.znacka
        Get
            Return "+"
        End Get
    End Property
End Class

jinak řečeno - Vlastnost name nám bude vracet slovní popis matematické metody, vlastnost znacka nám vrátí jako string znaménko metody a funkce operace spolkne dva sčítance (x a y) a do referenční proměnné vysledek uloží jejich součet. Návratovou hodnotou je buď prázdný řetězec značící, že výpočet proběhl vpořádku, nebo text chybové zprávy.

Stejně tak si můžeme (pod výše uvedený kód) doplnit ještě stejným způsobem třeba třídu pro odčítání:

Public Class Odcitani
    Implements IMathDual

    Public ReadOnly Property name() As String Implements PlugInterf.IMathDual.name
        Get
            Return "Odčítání"
        End Get
    End Property

    Public Function Operace(ByVal x As Single, ByVal y As Single, ByRef vysledek As Double) As String Implements 

PlugInterf.IMathDual.Operace
        vysledek = x - y
        Return ""
    End Function

    Public ReadOnly Property znacka() As String Implements PlugInterf.IMathDual.znacka
        Get
            Return "-"
        End Get
    End Property
End Class

16) Opět si můžeme celý projekt sestavit, aby se nám vytvořila naše první "zásuvná" knihovna

17) A nyní nastal čas tvorby vlastního kalkulátoru:

18) V solution exploreru opět zadáme add-> new Project, vybereme Windows Application a nazveme ji třeba Kalkulator

19) Formulář si upravíme na rozměr 500 x 300, umístíme na něj:

- Tebtbox, nastavíme u něj multiline na true, rozměr 108 x 242 a umístění na pozici 12;12 (hodnoty jsou, samozřejmě přibližné - takto se mi to samo chytlo okrajů. Rozměry udávám pouze proto, že nemám možnost ukázat, jak by to mělo vypadat a ať to máme alespoň přibližně stejné). Název nastavte třeba na TbxVypis.

Anchor nastavte na Top,Left,Bottom

- druhý textbox s názvem TbxDisplay, u něj nastavte trochu výraznější font (já nastavil Size na 20 a vlastnost Bold na True, vložte počáteční text 0 (nula) a polohu někde vpravo nahoře (já mám Location 126;12 a rozměr 354x38)

Anchor nastavte Top,Left,Right (aby nám to nautíkalo).

- ke spodní hraně formuláře si do pravého rohu přidejte dvě tlačítka standardní velikosti (tak, jak Vám to samy budou chytat zarovnávátka k okrajům), jedno nazvěte třeba BtnClear a napište na něj CLS, druhé nazvěte BtnRovno a napište na něj "=".

Pro obě tlačítka si nastavte Anchor na Bottom, Right

- jako poslední si na formulář přidejte Panel, kterým vyplňte (opět jak Vám to zarovnávátka zachytí) zbylý prostor mezi dvěma textboxy nahoře a vlevo a tlačítky dole. Anchor nastavte na všechny 4 směry a název panelu nechte klidně standardní. Ten panel je pouze pomocný - sem budeme umisťovat tlačítka jednotlivých funkcí).

No a můžeme přejít k tvorbě kódu:

20) Protože budeme opět potřebovat pracovat s naším rozhranním, stejně jako v minulém projektu, přidáme si i do tohoto projektu referenci na náš PlugInterf, včetně Imports PlugInterf na začátku kódu.

21) Začátek kódu by mohl vypadat následovně:

Imports System.IO
Imports System.Reflection
Imports PlugInterf

Public Class Form1

    'Seznam všech dostupných zásuvných modulů
    Private operacePrim As New List(Of IMathPrim)
    Private operaceDual As New List(Of IMathDual)

    Private registr As Single = 0
    Private operaceId As Integer = 0

Jak již bylo zmíněno, nejprve si naimportujeme potřebné jmenné prostory a poté si nadeklarujeme globální proměnné.

Dále budeme potřebovat nějaké kontejnery, ve kterých budeme uchovávat odkazy na nalezené matematické operace. Na rozdíl od příkladu pana Linharta jsem ty proměnné (jako silně typované kolekce) nadefinoval dvě - pro operace zpracovávající dva vstupní operandy a pro operace pracující pouze s jedním vstupem (každá skupina má své vlastní rozhranní, protože obsahuje jinak volanou funkci).

Dále jsem si nadeklaroval dvě proměnné jako zásobníky pro zapamatování zvolené operace a hodnoty prvního operandu

Nyní ošetříme událost Load formuláře.

V této události se nejprve pokusíme najít zásuvné moduly, které jsou k dispozici a poté doplníme uživatelské rozhranní (pro každou operaci přidáme na formulář odpovídající tlačítko):

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        nactiPluginy()

        doplnFormular()

    End Sub

A tady jsou jednotlivé metody:

Private Sub nactiPluginy()
        'Prohledá všechny dll soubory ve složce aplikace
        For Each file As FileInfo In New DirectoryInfo(My.Application.Info.DirectoryPath).GetFiles("*.dll", 

SearchOption.TopDirectoryOnly)

            Dim pluginAssembly As Assembly = Nothing
            Try
                'Pokusí se načíst knihovnu
                pluginAssembly = Assembly.LoadFrom(file.FullName)
            Catch ex As Exception
                'Při načítání knihovny došlo k chybě (např. nesprávný formát knihovny)
            End Try

            If pluginAssembly IsNot Nothing Then
                'Prohledá všechny typy v knihovně
                For Each t As Type In pluginAssembly.GetTypes()

                    'Pokud typ implementuje rozhraní zásuvného modulu (definovaného v projektu PluginInterf)
                    'vytvoří instanci tohoto typu a přidá jej do seznamu zásuvných modulů
                    'nejprve pro IMathPrim
                    If t.GetInterface(GetType(IMathPrim).FullName) IsNot Nothing Then
                        Dim plugin As IMathPrim = DirectCast(pluginAssembly.CreateInstance(t.FullName), IMathPrim)
                        operacePrim.Add(plugin)
                        ' Poté pro IMathDual
                    ElseIf t.GetInterface(GetType(IMathDual).FullName) IsNot Nothing Then
                        Dim plugin As IMathDual = DirectCast(pluginAssembly.CreateInstance(t.FullName), IMathDual)
                        operaceDual.Add(plugin)
                    End If
                Next
            End If
        Next
    End Sub

Metodu pro načtení Plug-inů jsem si, s laskavým svolením (nebo vlastně bez něj) půjčil od pana Linharta (stále jenom rozpracovávám jeho příklad, aby bylo patrné, jak by to bylo možné (celkem smysluplně) využít prakticky), a udělal jsem v něm pár úprav:

- vyhodil jsem kontrolní výpisy (ty mě teď nezajímají) a za druhé jsem nalezené typy zkoumal na implementaci obou mých rozhranní.

Po doběhnutí této metody již mám všechny nalezené plug-in třídy uložené v mých dvou kolekcích.

Metoda pro doplnění formuláře:

 Private Sub doplnFormular()

        ' duální operace
        Dim rozmerX = Panel1.Width / 5
        Dim pocatekY = 0

        For i As Integer = 0 To operaceDual.Count - 1

            Dim tlacitko As New Button
            With tlacitko
                .Left = (i Mod 5) * rozmerX
                .Width = rozmerX - 5
                .Top = Math.Floor(i / 5) * 30
                .Height = 23
                .Text = operaceDual(i).znacka
                .Tag = i

            End With
            Panel1.Controls.Add(tlacitko)
            AddHandler tlacitko.Click, AddressOf osetriDual
        Next

        ' primární operace
        pocatekY = Panel1.Height / 2

        For i As Integer = 0 To operacePrim.Count - 1

            Dim tlacitko As New Button
            With tlacitko
                .Left = (i Mod 5) * rozmerX
                .Width = rozmerX - 5
                .Top = pocatekY + Math.Floor(i / 5) * 30
                .Height = 23
                .Text = operacePrim(i).znacka
                .Tag = i

            End With
            Panel1.Controls.Add(tlacitko)
            AddHandler tlacitko.Click, AddressOf osetriPrim
        Next

    End Sub

Pro každou nalezenou třídu (matematickou funkci) vytvořím tlačítko, nastavím jeho vlastnosti a polohu (duální funkce řadím vedle sebe od levého horního rohu mého pomocného Panelu1, tlačítka pro jednooperátorové funkce řadím do dolní poloviny téhož panelu).

Do vlastnosti .tag každého tlačítka si uložím index konkrétní funkce v kolekci (pro pozdější jednodušší volání).

Přidám handler na ošetření stisknutí tlačítka a tlačítko přidám do kolekce Controlls daného panelu.

Toto celé provedu jak pro všechny nalezené funkce v kolekci operaceDual, tak i pro všechny v operacePrim.

No a Toť takřka celé, zbývá již jen ošetřit stisknutí funkčních tlačítek:

Private Sub osetriDual(ByVal sender As Object, ByVal e As System.EventArgs)

        operaceId = CType(sender, Button).Tag
        registr = CSng(TbxDisplay.Text)

        'zapíšeme do výpisu
        TbxVypis.Text &= registr & vbCrLf
        TbxVypis.Text &= operaceDual(operaceId).znacka & vbCrLf

    End Sub

Stisknutí tlačítka duální operace nedělá nic moc, protože vlastní výpočet je prováděn až po zadání druhého operandu a stisknutí tlačítka rovná se.

Pouze tedy provedeme nějaké kontrolní zápisy a "zapamatujeme" si první operand a Id pluginu odpovídajícímu stisknutému tlačítku.

Vlastní výpočet proběhne až po stisknutí tlačítka BtnRovno (jedno ze dvou, které jsme na form přidali sami):

 Private Sub BtnRovno_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnRovno.Click
        Dim vysledek As Double
        Dim resultat As String = operaceDual(operaceId).Operace(registr, CSng(TbxDisplay.Text), vysledek)
        TbxVypis.Text &= TbxDisplay.Text & vbCrLf & "=" & vbCrLf

        If resultat = "" Then
            TbxVypis.Text &= vysledek & vbCrLf
            TbxDisplay.Text = vysledek
        Else
            TbxVypis.Text &= resultat
            TbxDisplay.Text = resultat
        End If

        TbxVypis.Text &= "============" & vbCrLf & vbCrLf
    End Sub

Funkčnost je snad zřejmá.

Ošetření tlačítka pro jednooperátorové funkce udělá vše najednou:

Private Sub osetriprim(ByVal sender As Object, ByVal e As System.EventArgs)

        operaceId = CType(sender, Button).Tag

        Dim vysledek As Double
        Dim resultat As String = operacePrim(operaceId).Operace(CSng(TbxDisplay.Text), vysledek)

        'zapíšeme do výpisu
        TbxVypis.Text &= TbxDisplay.Text & vbCrLf
        TbxVypis.Text &= operacePrim(operaceId).znacka & vbCrLf
        TbxVypis.Text &= "=" & vbCrLf

        If resultat = "" Then
            TbxVypis.Text &= vysledek & vbCrLf
            TbxDisplay.Text = vysledek
        Else
            TbxVypis.Text &= resultat
            TbxDisplay.Text = resultat
        End If
        TbxVypis.Text &= "============" & vbCrLf & vbCrLf
    End Sub

A aby byla kalkulačka hotová, zbývá nám toliko ošetřit stisknutí druhého tlačítka, které jsme si na form dali sami a které tento form vyčistí:

 Private Sub BtnClear_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnClear.Click
        registr = 0
        operaceId = 0
        TbxVypis.Text = ""
        TbxDisplay.Text = "0"
    End Sub

Toť celé. Pokud jsem tam nenasázel moc chyb, tak můžete projekt zkompilovat a spustit, a...

...a vidíte, že máte prázdnou kalkulačku, bez jakýchkoliv funkcí. Ale to je zcela vpořádku, protože jsme programu nedali k dispozici žádný plug-in. Proto vypněte kalkulačku, a do adresáře s jejím spustitelným souborem nakopírujte knihovnu s našimi dvěma pluginy - Matika01.dll.

A spusťte kalkulačku znovu - a pokud nedošlo k nějaké chybě, už byste na ní měli mít první 2 funkční tlačítka.

Chvíli si to otestujte a pak to celé zavřete (včetně visual Basicu).

No a aby byl smysl Zásuvných modulů ještě zřejmější, pohrajeme si teď na "externího vývojáře".

Spusťte si VB (nebo VS), a vytvořte si zcela nový projekt typu Class Library. Jako jméno dejte třeba Matika02 a do názvu Solution si dejte třeba Pluginy.

A dále už to znáte:

V Solution Exploreru si přejmenujte Class1.vb na Matika2.vb, do referencí si přidejte naši známou PlugInterf.dll, a do kódu úplně nahoru naimportujte jmenný prostor našich plugin referencí:

Imports PlugInterf

A zkusíme si tentokrát udělat něco pro jeden operátor, třeba druhou odmocninu (abychom si ukázali i chybová hlášení).

Změňte si název třídy a naimplementujte si potřebné rozhranní:

Imports PlugInterf

Public Class DruhaOdmocnina
    Implements IMathPrim

Zase nám to vytvoří celou kostru třídy a my ji jen smysluplně doplníme. Třeba takto:

Imports PlugInterf

Public Class DruhaOdmocnina
    Implements IMathPrim

    Public ReadOnly Property name() As String Implements PlugInterf.IMathPrim.name
        Get
            Return "Druhá odmocnina"
        End Get
    End Property

    Public Function Operace(ByVal x As Single, ByRef vysledek As Double) As String Implements 

PlugInterf.IMathPrim.Operace
        vysledek = 0
        If x < 0 Then Return "ODMOCNINA ZÁP. ČÍSLA!"
        vysledek = Math.Sqrt(x)
        Return ""
    End Function

    Public ReadOnly Property znacka() As String Implements PlugInterf.IMathPrim.znacka
        Get
            Return "x^½"
        End Get
    End Property
End Class

Vysvětlení snad netřeba.

Samozřejmě si můžete do stejného souboru nasázet i další třídy pro další funkce (dokonce můžete v jednom souboru klidně kombinovat funkce pro dva operandy s funkcemi pro jeden - jenom musíte vždy použít správné rozhranní).

Celý projekt si sestavte (menu->Build->Build matrika02).

(nelekněte se, pokud Vám to vyhodí hlášku ohledně kódování - v označení funkce jsem použil znak 1/2, což je unicode, tak to klidně nechte systém překódovat).

Opět můžete Visual basic ukončit a projekt uzavřít a...

... a to hlavní právě nastává.

Najděte si (průzkumníkem) na disku právě vytvořenou knihovnu Matika02.dll (pokud jste používal názvy dle mé rady, tak ji naleznete v projektech v podadresáři Pluginy\Matika02\bin\Debug a

překopírujte ji do adresáře, ve kterém máte zkompilovánu kalkulačku z minulého projektu (opět, pokud jste použil doporučené názvy, pak je to v kalkulator\Kalkulator\bin\Debug).

No a v tomtéž adresáři byste měl nalézt i spouštěcí soubor celé kalkulačky (Kalkulator.exe), tak jej pokepáním spusťte.

A aniž byste jakkoliv do vlastního programu kalkulátoru zasahoval, měl byste ho mít rozšířený o další funkci.

A to je krása, smysl a oblast vhodného nasazení Plug-inů!

Za domácí úkol si můžete vytvořit pár dalších zásuvných modulů a můžete je dát do diskuse k dispozici ostatním, stejně tak mohou pár (snad jiných :-) funkcí přidat i ostatní kolegové, kteří se v tom budou chtít pocvičit, no a tak uzříte další výhodu Plug-inů - mohou být vyvíjeny nezávisle na sobě v komunitě, čímž silně akcelerujete vývoj takovéhoto projektu!

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Tak to vypadá, že jsem server neshodil :-))), jenom těch patnáct minut se trošičku protáhlo :-(((

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

http://www.storiesoftheoldwest.com/ cheap auto insurance 046 http://www.ldsorganistblog.com/ life insurance quotes :] http://www.chariscreationsgf.com/car-ins... nj car insurance 504286

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Máte můj respekt!

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Děkuji, toho si skutečně vážím.

Hlavně jestli to alespoň někomu pomůže, já se aspoň donutil si to vyzkoušet :-)))

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Muj respekt máte taktéž,obdivuji naši snahu a to jak tomu všemu rozumíte (zde by se již o článku dalo mluvit,a to jak délkou tak i srozumitelnym poučnym obsahem) nicméně mi VS dělá naschvály....po kliknutí na "buid PlugInterf" se mi sice zobrazí knihovna PlugInterf ale jmenuje se PligInterf1.dll nachází se vždy v Release a nikoli Debug,a nejspíš proto po naimportování do kalkulačky vyhodí kalkulačka chybu(kod je v pořádku)"Error 1 Handles clause requires a WithEvents variable defined in the containing type or one of its base types. C:\Documents and Settings\romoli.ANDY\Dokumenty\Visual Studio 2005\Projects\kalkulacka\kalkulacka\Form1.vb 106 100 kalkulacka"

A já ať se snažím jak chci ne a ne ji spustit.

Ještě dotaz,jak importujete knihovny? a jak se dají zase odstranit,já ji jen přetáhnu do VS ale jak ji odstranit (pokud třeba naimportuji špatnou)sem nepřišel,nic jako delete,clear atd tam nemám.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

K tomu umístění .dll. Pokud se kouknete na kartu nastavení projektu (tam kde jsme linkovali ty reference), tak je tam záložka s označením Compile. A tady se dá nastavit, v jakém módu se Vám bude aplikace kompilovat (Configuration) a buď tam je nastaveno Debug, nebo Release. Ta Release kompilace je "ostrá" - je optimalizovaná a očesaná o vše nepotřebné, ta debug v sobě nese ještě nějaké informace a funkce pro případnou kontrolu chodu aplikace (nevím co všechno přestě, jedna z věcí o kterých vím a které občas využívám jsou kontrolní výpisy do debug-výstupu).

Ale osobně si myslím, že z hlediska tohoto příkladu by v tom neměl být zásadní rozdíl - nejzásadnější je, že defaultně se debug verze kompilují do podadresáře bin\debug a releas verze se překvapivě kompilují do bin\release.

Jinak s tím importem knihoven - zkuste dát dotaz do vyhledávání, nekolikrát se to tady v diskusi již řešilo (obecný import knihoven do projektu jako takového). Jinak pokud něco do projektu přidáte, melo by to jít v Solution Exploreru odstranit - pod pravým tlačítkem myši máte příkaz Remove.

Pokud ale jde o náš příklad, je situace trochu jiná. Nevím, možná by to mohlo jít i přes VS & jeho solution explorer a volbu přidat, tady ale pozor na to, že pokud jakýkoliv soubor přidáte v průběhu tvorby solution, tak se Vám to fyzicky přidá do adresáře se solution (nebo projektem), ale ne do adresáře, ve kterém bude šlapat ta zkompilovaná aplikace. Tady pozor, nezapomínat pro takto vložený soubor nastavit jeho kopírování do výstupního adresáře ( Copy allways resp. Copy if newer, nebo tak nějak)

Zpátky tedy k našemu příkladu - krása této technologie spočívá právě v tom, že naše knihovny jsou "dynamicky napojované", tedy napojované až za běhu aplikace.

Jinak to ani nejde, protože ve chvíli tvorby té hlavní aplikace vůbec nevíte, jaké knihovny (s plug-iny) budou existovat.

Proto taky žádné knihovny do projektu "neinportujte", jenom je jednoduše (v průzkumníku) nakopírujte do adresáře, ve kterém máte spustitelný program - on si je už vyhledá a napojí sám.

No a ještě k té chybě. S tím označením překontrolujte, jestli tam někde už stejně označenou knihovnu nemáte (nevím, jestli by to v případě konfliktu názvů nové verze indexovalo, nebo by to nahlásilo nějaké upozornění). Pro náš příklad by ale mělo být úplně jedno, jak Vám systém Vaši knihovnu nazve - vemte co máte a nakopírujte na potřebné místo (program si s tím již poradí).

Teď ještě koukám, že Vám to přeznačuje knihovnu s rozhraním - tam je situace trošičku jiná, tam referenci na rozhranní skutečně importujete sám, takže tam musíte na kartě "References" vybrat referenci na správný (existující) soubor - ani jinou možnost Vám to nenabídne) a na začátku kódu programu musíte správně napsat ten imports (u mne PlugInterf). Ale zase, pokud napíšete "Imports" a dáte mezeru, tak Vám systém nabídne, co všechno můžete importovat a vyberte si ten správný "PlugInterf...). Ale opět nic nikam nepřetahujete ani nepřidáváte - pouze tyto dva kroky, systém si potřebný soubor dá kam potřebuje sám).

Jestli je ta Vaše chyba způsobena až tím, jak do systému nakopírujete knihovnu s Plug-iny, to můžete zjistit velice jednoduše tak, ře půjdete do adresáře, v ekterém máte zkompilován program Kalkulacka (buď bin\debug nebo bin\release) a smažte z tohoto adresáře vše, co nezačíná "Kalkulator..." případně "Pluginterf...". No a pokud spustíte Kalkulatoor.exe, měla by se Vám spusti prázdná kalkulačka.

Pak si do tohoto adresáře vraťte ty knihovny s pluginy (nakopírujte průzkumníkem) a vyzkoušejte.

Podle textu Vaší hlášky bych ale odhadoval spíše, že máte nějaký problém s deklarací nějakého controlu. Zkontrolujte si, prosím, názvy (jestli jste se nepřeklepl třena v názvu tlačítka BtnRovno,

případně napište, u jakého řádku Vám to tu chybu vyhazuje.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Vše se jen kopíroval (kuli oněm překlepum) takže to je vyloučeno a řádek chyby je psán v té chybové hlášce (je také zkopírován sem) ale program jak to VS dělalo mi neskočí do kodu a neukáže na chybu jen to hodí tuto hlášku.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

A spouštíte to přímo jako exe, nebo uvnitř studia?

Zkuste (pokud to tak náhodou neděláte) otevřít projekt kalkulátor ve VS, v solution exploreru klikněte pravým tlačítkem myši na název projektu Kalkulator, vyberte možnost "set as startup project", pak na kartě nastavení tohoto projektu za záložce "Compile" nastavte Configuration na Debug, totéž nastavte na záložce "Debug". Na záložce Compile ještě zkontrolujte, jestli dole nemáte zaškrknuto Disable all warnings.

No a zkuste to spustit (přímo ve VS zelenou šipkou).

Jestli Vás to ani nyní nehodí s chybovou hláškou do kódu, pak tedy nevím a bude Vám muset poradit někdo, kdo tomu (na rozdíl ode mne) skutečně rozumí.

Ještě ale k té kontrole chyb - to, že jste to celé zkopíroval ještě nemusí znamenat, že to máte skutečně bez chyby!

Jednak jsem já při kopírování mohl udělat nějakou drobnou chybku (občas mi vypadne při kopírování první nebo poslední písmenko), pokud Vám kompilátor přejmenoval soubor s rozhranním, pak musíte tuto změnu jména zohlednit např. v těch Imports.

No a v neposlední řadě je tam fůra možností, jak se překlepnout v místech, která nemůžete zkopírovat.

Třeba názvy vkládaných záležitostí, ty jste musel opsat z mého textu. A ne vždy jste opisoval úplně přesně (což není chyba(!), jenom se to pak musí případně upravit i v kódu), o čemž svědčí i Váš výpis, ze kterého je zřejmé, že jste si jak Solution, tak i projekt vlastního počítacího stroje nazval "Kalkulacka", kdežto já to měl nazváno "Kalkulator"

No a protože Vaše chybové hlášení vypisuje, že "konstrukce handles vyžaduje proměnnou deklarovanou s klauzulí withevents" a pokud čísla za tímto hlášením udávají řádek a sloupec (nevím to, ale odhaduji, že by to tak mohlo být), pak velice blízko udané pozice (106.ř.,100.sl.) mám já v kódu právě začátek eventu proměnné BtnRovno.Click (já to mám na 100.ř a 100. sloupci, což může být způsobeno odmazáním několika prázdných řádků, či podobnou úpravou).

Proto jsem odhadoval, jsetli jste náhodou nedal tlačítku pro výpočet jiný název.

Zkuste na formuláři na toto tlačítko 2x poklepat, ono Vám to vytvoří konstrukci ošetření události a zkopírujte sem celý první řádek (definici metody). Pokud za slůvkem Handles budete mít jiný text než "BtnRovno.click", pak místo BtnRovno napiště to, co Vám to napsalo v té připravené metodě (a tu novou metodu smažte).

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Bohužel jsem chybu nenalezl,rozhodl jsem se až bude čas,celý program napsat znova,dám vědět.Děkuji za pomoc.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Jak rozšířit aplikaci o zásuvné moduly (Plug-in)

Dobrý den,

mám takový dotaz, jak by se dal vytvořit vlastní panel s prvky a procedurami, který by se do aplikace načítal jako plugin (.dll) - a rozšířil by tak Forms.TabControl o nový panel?

Děkuji..

nahlásit spamnahlásit spam 0 odpovědětodpovědět

To by nebyl problém, stačí jen vymyslet to rozhraní, pomocí kterého by komunikovala aplikace a plugin. V úplném základu by stačila v tomto rozhraní jedna metoda, která by vracela hodnotu typu TabPage.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Děkuji za odpověď.

Přeci jen mohu požádat o ukázku?

Děkuji

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Jak rozšířit aplikaci o zásuvné moduly (Plug-in)

Dobrý den,

přečetl jsem si celý strom diskuse k dynamicky nahrávaným pluginům,ale i přes dlouhou snahu se mi nedaří postup zprooznit.

Mám aplikaci, pro kterou potřebuji realizovat různé exporty. Vytvořil jsem proto samostatnou aplikaci (WinForm), která používá 2 vstupní property (sql connection, sql dotaz).

Pokud tuto zkompilovanou aplikaci jako DLL vložím již do referencí hlavní aplikace, vše funguje normálně.

Pokud se pokouším nahrát dll za chodu, neustále program hlásí chybu, že nelze přetypovat něco na něco.

Mám vytvořený interface (identický v obou aplikacích) a přesto nelze jen typ přetypovat na totožný druhý (z pluginu).

Chyba nastává při volání directcast:

If plugMopro IsNot Nothing Then
            'Prohledá všechny typy v knihovně
            For Each t As Type In plugMopro.GetTypes()
                'Pokud typ implementuje rozhraní zásuvného modulu (definovaného v projektu PluginInterface)
                'vytvoří instanci tohoto typu a přidá jej do seznamu zásuvných modulů
                If t.GetInterface(GetType(IPlugMOPRO).FullName) IsNot Nothing Then
                    Dim plugin As IPlugMOPRO = DirectCast(plugMopro.CreateInstance(t.FullName), IPlugMOPRO)
                    Console.WriteLine("Nalezen zásuvný modul: " & plugin.PlugName)
                End If
            Next
        End If

Můžete mi někdo poradit, kde může být chyba?

Nefunguje mi to ani s primitivním pluginem, který by měl vrátit pouze jeho název.

Stejná chyba: nelze přetypovat ...

Díky

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Jak rozšířit aplikaci o zásuvné moduly (Plug-in)

Připojuji se k poděkování za jednoduchý mustr jak na to. Prostoru Reflection jsem se vždy obloukem vyhýbal, takže ještě jednou moc děkuji.

Petr Žizka

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Jak rozšířit aplikaci o zásuvné moduly (Plug-in)

Zdravím,

předně díky za skvělý a velice názorný příklad ohledně používání Pluginů.

Měl bych jeden dotaz. Co když bude mít třída, kterou chci následně použít jako plugin parametrizovaný konstruktor? Je možné nějakým způsobem při vytváření instance třídy jako pluginu předat jejímu konstruktoru parametr?

Díky za případnou odpověď.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Příklad který jsem uvedl počítá s tím, že třída implementující IMathPlugin, tedy vlastní zásuvný modul bude obsahovat konstruktor bez parametrů. Může samozřejmě obsahovat další konstruktory s libovolným počtem libovolných parametrů, ovšem výchozí konstruktor bez parametrů je nutný pro použitou metodu CreateInstance. Program je navržen tak, že předem nezná konkrétní typy které tvoří zásuvné moduly a tudíž nemůže vědět jaké parametry konstruktor daného typu očekává. V definici rozhraní konstruktor definovat nelze. Pomocí Reflection jde samořejmě zjistit jaké konstruktory s jakými parametry daný typ obsahuje, ale v praxi by to k ničemu nebylo. V případě zájmu můžu uvést příklad.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Díky za odpověď. Příklad nemusíte uvádět. Už jsem to tak trochu obešel a v mém řešení používám pro vytváření instance třídy Activator, kterém mimo jiné předávám i pole parametrů pro konstruktor.

V každém případě děkuji za Vaši reakci.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Jak rozšířit aplikaci o zásuvné moduly (Plug-in)

Zkousel jsi uz AddIn co je v .net 3.5?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Prominte asi to dobře nechápu.Mohl by jste mi to prosim někdo popsat nebo názorně ukázat na Form? Děkuji,já už se s tim nevim rady.

nahlásit spamnahlásit spam -2 / 2 odpovědětodpovědět

Takže jsem to dle slibu zkusil a není to zas až tak složité.

Je to přesně tak, jak píše pan Linhart a jak jsem odhadoval výše.

Takže postup:

- Vytvořte si novou WinForm aplikaci

- do podadresáře této Vaší nové aplikace bin\debug nakopírujte z příkladu pana Linharta všechny 3 dll knihovny (pluginInterface, BasicMath a AdvancedMath) (samozřejmě si můžete tyto dll knihovny vytvořit nově přímo ve Vašem novém projektu, zkompilovat je a pak šoupnout do výše uvedeného adresáře, ale je to zbytečné - alespoň si ověříte "přenositelnost" Interface i Plug-inů)

- do referencí si přidejte právě vámi nakopírovaný PluginInterface.dll

- chcete-li si to jenom jednoduše vyzkoušet, dejte si na Form1 jedno tlačítko a jeden TextBox. U tohoto Textboxu nastavte Multiline na True

- přejděte na záložku s kódem Form1.vb a jak bylo řečeno již výše, před řádek "Public Class Form1" přeneste z projektu pana Linhatra ty 3 importy

- protože se na pluginy budete odvolávat v celé aplikace, je zapotřebí proměnnou plugins nadefinovat jako globální, proto hned jako první řádek ve třídě Form1 dejte tuto deklaraci.

Začátek tedy bude vypadat následovně:

Imports System.Collections.Generic
Imports System.IO
Imports System.Reflection

Public Class Form1

    'Seznam všech dostupných zásuvných modulů
    Private plugins As New List(Of IMathPlugin)

No a pokud jste postupoval stejně jako já, objevíte první zradu: IMathPlugin Vám s nejvyšší pravděpodobností Studio podtrhne.

Pokud ale použijete postup, který jsem Vám radil minule, a najedete myší na to podtržení, vysvětlí Vám to chybu, a totiž že IMathPlugin není definováno. Současně se Vám u této hlášky zobrazí vykřičník, který když si rozbalíte, tak Vám to dokonce nabídne řešení - "změň IMathPlugin na OL.SimplePluginsExample.IMathPlugin". Pokud tuto volbu zvolíte, tak se Vám deklarace přepíše s touto celou cestou k dané třídě:

 Private plugins As New List(Of OL.SimplePluginsExample.IMathPlugin)

Jinak řečeno, tímto postupem velice jednoduše zjistíte chybu, a to, že deklarace nemůže najít Typ IMathPlugin, protože neví, kde by ho měla hledat. Buď tedy musíte tento typ zadat včetně umístění (jmenného prostoru, ve kterém se nalézá), nebo, ještě lépe, abyste to nemusel opisovat stále dokola celé, tento jmenný prostor si naimportujte a pak už ho kompilátor bude prohledávat a Vám stačí napsat už jenom konečný název požadovaného typu.

Takže k importům na začátku přidejte ještě jeden:

Imports System.Collections.Generic
Imports System.IO
Imports System.Reflection
Imports OL.SimplePluginsExample

a pak už je i původní zápis deklarace bezproblémový.

Vzal jsem to trochu polopaticky, ale jenom proto, abyste viděl, jak můžete fůru chyb objevit sám a taky, co myslel pan Linhart tím, že vše se tady již probíralo - šlo o takovéto základní postupy, práce s namespace apod.

No a teď by Vám už nemělo nic stát v cestě to celé překlopit do winformu.

Těch možností je samozřejmě fůra, já to v rychlosti zkusil následovně:

Ty deklarace a načtení Plug-inů bych asi normálně dal do Form1_load, ale protože jsem chtěl pro názornost zachovat ty kontrolní výpisy, dal jsem to do procedury, která se provádí až když je už form vytvořený, tedy do Form1_Shown.

No a tu vlastní výkonnou část jsem dal pod tlačítko.

Ještě jednu změnu jsem udělal - místo kontrolních výstupů na konzoli vše vypisuji do TextBoxu:

Imports System.Collections.Generic
Imports System.IO
Imports System.Reflection
Imports OL.SimplePluginsExample

Public Class Form1

    'Seznam všech dostupných zásuvných modulů
    Private plugins As New List(Of IMathPlugin)


    Private Sub Form1_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown

        'Prohledá všechny dll soubory ve složce aplikace
        For Each file As FileInfo In New DirectoryInfo(My.Application.Info.DirectoryPath).GetFiles("*.dll", SearchOption.TopDirectoryOnly)

            Me.TextBox1.Text &= vbCrLf & "Hledání kompatibilních zásuvných modulů v souboru " & file.Name & "..."

            Dim pluginAssembly As Assembly = Nothing
            Try
                'Pokusí se načíst knihovnu
                pluginAssembly = Assembly.LoadFrom(file.FullName)
            Catch ex As Exception
                'Při načítání knihovny došlo k chybě (např. nesprávný formát knihovny)
            End Try
            If pluginAssembly IsNot Nothing Then
                'Prohledá všechny typy v knihovně
                For Each t As Type In pluginAssembly.GetTypes()
                    'Pokud typ implementuje rozhraní zásuvného modulu (definovaného v projektu PluginInterface)
                    'vytvoří instanci tohoto typu a přidá jej do seznamu zásuvných modulů
                    If t.GetInterface(GetType(IMathPlugin).FullName) IsNot Nothing Then
                        Dim plugin As IMathPlugin = DirectCast(pluginAssembly.CreateInstance(t.FullName), IMathPlugin)

                        Me.TextBox1.Text &= vbCrLf & "Nalezen zásuvný modul: " & plugin.Name

                        plugins.Add(plugin)
                    End If
                Next
            End If
            Me.TextBox1.Text &= vbCrLf
        Next
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        If plugins.Count > 0 Then

            Me.TextBox1.Text &= vbCrLf & "Spuštěn test zásuvných modulů..."

            'Vytvoří náhodné vstupní parametry pro matematické operace
            Dim randomizer As New Random()
            Dim x As Single = randomizer.Next(0, 100)
            Dim y As Single = randomizer.Next(0, 100)

            Me.TextBox1.Text &= vbCrLf & String.Format("Vytvořeny náhodné vstupní parametry: {0}, {1}", x, y)

            'Pro každý zásuvný modul v seznamu se pokusí provést jeho matematickou operaci
            For Each plugin As IMathPlugin In plugins
                Try
                    Me.TextBox1.Text &= vbCrLf & String.Format("{0}: {1}", plugin.Name, plugin.PerformMathOperation(x, y))
                Catch ex As Exception
                    'Zde je možné ošetřit případné vyjímky v zásuvném modulu (např. vyjímka vzniklá při dělení nulou)

                    Me.TextBox1.Text &= vbCrLf & String.Format("V zásuvném modulu {0} došlo k chybě: {1}", plugin.Name, ex.Message)

                End Try
            Next
        Else
            Me.TextBox1.Text &= vbCrLf & String.Format("Ve složce ""{0}"" nebyly nalezeny žádné zásuvné moduly.", My.Application.Info.DirectoryPath)
        End If
        
    End Sub
End Class

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Děkuji za vyčerpávající vysvětlení,konečně to snad chápu,ještě to musim trošku poladit ale to už jsou jen kosmetické upravy,vy by ste měl psát články,číst je by byla jedna radost.

nahlásit spamnahlásit spam -2 / 2 odpovědětodpovědět

Dík za uznání (hlavně pokud Vám to alespoň trochu pomohlo), leč já psát články nemohu, a to hned ze dvou důvodů.

Jednak bych brzy zahltil webový server, neb, jak jste si jistě všiml, mé výplody bývají povětšinou dosti obsáhlé - jsem zvyklý uvažovat v souvislostech, což se nemalou měrou promítá i do mého písemného projevu - místo věty holé, zachycující pouze podstatu problému, se snažím popsat i maximum souvislostí a pak to tak dopadá.

No a druhým důvodem, tím zásadnějším je, že neumím :-(

Bohužel jsem pouhopouhým začátečníkem v oblasti programování (a také jím asi zůstanu), takže více, než na osvětlení těch nejjednodušších a nejzákladnějších problémů zkrátka a jednoduše nemám.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Priklanam sa k Romanovi a navrhu aby ste aj vy zacali pisat clanky. Vase rady (teda nielen vase ale vsetkych skusenejsich) su pre Nas ako zaciatocnikov velmi cenne a velmi radi si precitame polopatisticky napisany navod na nejaku aplikaciu.

Velmi by som uvital keby sa kazdy tyzden/mesiac vytvorila nejaka aplikacia ktora by bola zamerana na konkretne funkcie jazyka napr. praca s textom praca so subormi, plugini. Napriklad by sa mi velmi pacil clanok o aplikacii ktora by mala jednoduche okno s nastaveniami ktore by si pamatala po vypnuti a dokazala by ich nacitat po zapnuti atd...

Pekny den

dodo

nahlásit spamnahlásit spam 0 odpovědětodpovědět

http://blog.vyvojar.cz/exavera/archive/2...

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Nezkoušel (ale vyzkouším). Každopádně v .NET 2.0 nic podobného není a proto vznikl tento článek.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Diskuse: Jak rozšířit aplikaci o zásuvné moduly (Plug-in)

Zdravím,

chtěl bych Vám velmi poděkovat za skvělý článek. Dneska jsem se rozhodl, že jej pochopím a to se mi i podařilo (a nebylo to nijak těžké), Váš článek mi usnadnil skutečně mnoho práce a přinesl další zkušenosti, za to všechno Vám velice děkuji!

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Ještě bych se k článku rád na něco zeptal:

Vytvořil jsem si aplikaci na kreslení. Ta mi nabízí určité nástroje, které chci řešit přes pluginy. Každý plugin reprezentuje jeden nástroj. Samozřejmě ale můžu v obrázku použít jeden nástroj víckrát, tzn., že jsem si v PluginInterface vytvořil třídu Base (prázdnou), jejíž vlastnosti pak budou odpovídat vlastnostem toho kterého nástroje. když na obrázek v hlavním programu použiju nástroj, vytvoří se instance třídy Base z pluginu, který ten nástroj reprezentuje. Pak, když Me.Invalidated apod. se mi obrázek překresluje a já si projdu všechny instance tříd Base v nástrojích které jsem na obrázek použil a zeptám se jich na Draw(). Funkce draw si bere parametr Data As Base, podle toho nakreslí obrázek a ten mi vrátí. Výsledkem volání těch instancí třídy Base je několik bitmap, které pak naplácám na sebe a mám finální obráztek, který chci.

Teď (po tom slohu) k jádru problému: každý nástroj má jiné vlastnosti (třeba čtverec má startovní pozici a rozměry, ale beziérova křivka má kolekci bodů a žádné rozměry), tak jsem si řekl, že si v každém pluginu vytvořím tu třídu Base, která bude implementovat tu z rozhraní. Ta bude mít určité vlastnosti a tahle třída se pak předá jako parametr pro Draw. (Já vím, je to zdlouhávé, hend budu u kódu). Jenže Visual Basic mi řekl, že třía base v pluginu může jenom dědit z třídy Base v rozhraní a ne ji implementovat. Jenže když jí jenom podědí, parametr do Draw, tedy Data, pak dostane instanci třídy Base z rozhraní a ta nemá žádné vlastnosti. Ty má mít třída Base v pluginu. Problém je tedy ten, jak můžu udělat to, že každý plugin bude obsahovat tu třídu, kterou bude požadovat jako parametr pro Draw?

Pochopím, pokud nepochopíte a zkusím to tedy vysvětlit na kódu:Rozhraní:

Imports System.Drawing
Public Interface ITool
    ReadOnly Property Name() As String
    Function Draw(ByVal Data As Base) As Bitmap
    Class Base
        ' Tohle je ta třída, kterou dostává Draw()
    End Class
End Interface

Plugin:

Imports System.Drawing

Public Class Tool
    Implements [Interface].ITool ' plugin implementuje rozhraní

    Public Function Draw(ByVal Data As [Interface].ITool.Base) As Bitmap Implements [Interface].ITool.Draw
    ' fce Draw, která bere Data, což je třída Base, bohužel ale ta třída z rozhraní
        Dim b As New Bitmap(128, 128)
        Dim g As Graphics = Graphics.FromImage(b)
        g.Clear(Color.Red)
        g.DrawString("TEST", New Font("Arial", 20, FontStyle.Bold, GraphicsUnit.Pixel), New SolidBrush(Data.Color), New Rectangle(0, 0, 128, 128), New StringFormat With {.Alignment = StringAlignment.Center, .LineAlignment = StringAlignment.Center})
        Return b ' vrací bitmapu s textem "TEST"
    End Function

    Public ReadOnly Property Name() As String Implements [Interface].ITool.Name
        Get
            Return "Test"
        End Get
    End Property

    Public Class Base ' a já potřebuji, aby tato třída byla předána jako parametr pro Draw()
        Private _color As Color = Color.Black
        Public Property Color() As Color
            Get
                Return _color
            End Get
            Set(ByVal value As Color)
                _color = value
            End Set
        End Property
    End Class
End Class

Když píšu plugin, parametr Data v Draw se chová jako třída z rozhraní, nikoliv jako třída z pluginu.

Dovolte mi omluvit se za tak strašně dlouh příspěvek, nechtěl jsem Vás s tím obtěžovat, ale už si s tím vůbec nevím rady. Díky

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Tak odpovědi již netřeba. Řešení je sice trochu prasácké, ale funkční, využívám totiž Late Bindingu.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Vhodnější by bylo udělat parametr metody Draw jako pole parametrů typu Object, do kterého by potom šlo předat libovolný počet libovolných parametrů a uvnitř metody potom kontrolovat, zda-li je parametr takového typu jako je očekáváno a v opačném případě vyvolat vyjímku.

'Implementace pro kreslení bodů
Public Function Draw(ByVal ParamArray params As Object) As Bitmap
  If params.Length <> 2 Then
    Throw New ArgumentOutOfRangeException("Počet parametrů nesouhlasí.", "params")
  End If
  If (TypeOf(params(0)) IsNot Point) AndAlso (TypeOf(params(1)) IsNot Point) Then
    Throw New ArgumentException("Jeden z parametrů není typu Point.", "params")
  End If
  'Nakreslit body
End Function

'Použítí:
Dim bmp As Bitmap = Draw(New Point(0, 0), New Point(1, 1))

'Implementace pro kreslení barvy
Public Function Draw(ByVal ParamArray params As Object) As Bitmap
  If TypeOf(params(0)) IsNot Color Then
    Throw New ArgumentException("Parametr není typu Color.", "params")
  End If
  'Použít barvu
End Function

'Použítí:
Dim bmp As Bitmap = Draw(Color.Blue)

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Musím říct, že tohle řešení je opravdu lišácké, to by mě nenapadlo a děkuji za něj.

Nicméňe zůstanu u svého způsobu, jelikož v aplikaci využívám prvek PropertyGrid, krerý si bere tu třídu představující data pro Draw() a tím, že můžu ty parametry předat jako instanci třídy mi odpadlo dost práce s vytvářením UI - prvek PropertyGrid je skutečně všestraný.

I tak Vám děkuji za odpověď

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Jak rozšířit aplikaci o zásuvné moduly (Plug-in)

Mnohokrát díky za článek, je super.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Jak rozšířit aplikaci o zásuvné moduly (Plug-in)

Ještě si dovolím drobný rýpanec. Slovo výjimka nemá nic společného s jímkou, jak by se snad mohlo zdát podle opakovaně užívané verze "vyjímka" v komentářích v programu.

Nicméně je to jen drobná kosmetická vada na jinak poučném příkladu.

nahlásit spamnahlásit spam -2 / 2 odpovědětodpovědět

Omlouvám se ještě dotaz,znamená to že knihoven jako je PluginInterface.dll mohu mít v referencích více a každá muže mít své vlastní zásuvné moduly nebo je lebší aby byla "univerzální" a k ní se mohlo přidávat víc než jen "matematika"?

A ještě jedna ...doposud sem nepřišel na to jak tyto konkrétní moduly použít,vím že jsou jen demonstrativní ale jak je mohu použít?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

PluginInterface je pouze knihovna, která obsahuje definici rozhraní pluginu, žádné konkrétní pluginy. Těchto rozhraní můžete mít samozřejmě více v jedné knihovně, každé pro určitou kategorii zásuvného modulu (matematické, grafické, atd.). Využití pluginů je takřka neomezené, matematika byla pouze jednoduchý příklad který mě zrovna napadl. V praxi je dobrý nápad například aplikace V. Langera, který by pluginy používal pro různé přechody mezi obrázky. Každý plugin bude obsahovat určitý typ přechodu (jako jsou přechody mezi snímky v PowerPointu). Do budoucna tak bude moct kdykoliv rozšířit aplikaci o nový přechod bez nutnosti rekompilace hlavní aplikace.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Je to asi užitečné a ulehčuje to práci ale ještě stále nevim jak s tim pracovat,moduly mi sice umožnili použít násobilku ale já ji neumim uvést do praxe,mám "x" a "y" a modul programu umožní je třeba násobit ale unikí mi jak třeba program tu operaci probede kdyš sem ji nepřidělil třeba tlačítko nebo textbox.To mi právě vrtá hlavou,nemohl by na tento článek navazovat nějaký který by osvětlil používání? Kupříkladu budu mít program co neumí absolutně nic a já po něm budu chtít (po formuláři) aby třeba ,po spuštěnía zkontrolování všech modulu, spustil Mp3 v PC na daném místě nebo aby přisal na svuj formulář nějaký prvek...jde to?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Myslím že chcete za každou cenu použít pluginy i tam, kde to není vhodné. Musíte se zaprvé rozmyslet, jakou doplňující/rozšiřující funkčnost by měly mít. Pluginy rozhodně nejsou určeny k tomu, aby kompletně zajišťovaly funkčnost aplikace. Jinak samozřejmě klepnutím na tlačítko lze zavolat jakoukoliv metodu nebo vlastnost pluginu, protože to je třída.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Ten Váš požadavek není zcela ideální (myslím s tím MP3).

V prvé řadě jde o to pochopit účel zásuvných modulů.

I když by to asi teoreticky možné bylo, tak se tyto moduly nepoužívají pro doplnění nějaké zcela nové funkčnosti do programu, ale pro rozšížení té stávající.

Proto definice, že máte program, co neumí absolutně nic a natažením pluginů Vám to přehraje MP3 skladbu, spočítá daňové přiznání a v případě vhodného plug-inu si na tom třeba ještě zahrajete 3D střílečku, tak taková představa je zcela zcestná. Totiž především ten program, který případné plug-iny bude schopen využít, toho musí umět docela dost (pozor, nezaměňovat s tím, že pz pohledu uživatele "sám nic neumí a nedělá).

A taky je zapotřebí zkoumat, nakolik je pro naše představy vhodná a účelná aplikace plug-inů. (Pokud zůstanu u Vašeho požadavku, jaký má smysl aplikace, která "neumí nic", jaký tedy má význam přes ni vykonávat funkcionalitu zásuvnývh modulů pro všechny výše vyjmenované činnosti, proč jenom prostě nespustit samostatný program na přehrávání MP3, samostatný účetní program,...)

Takže ve Vašem příkladě by bylo spíše na místě:

- Vytvořit bázovou aplikaci, která umí přehrát hudbu z nějakého rozumného formátu.

- jako pluginy si vytvořit třídy, které budou mít metody pro převod speciálních hudebních formátů do tohoto Vámi zvoleného "rozumného" formátu

- jako další pluginy si vytvořit třídy, které budou mít třeba metodu pro vytváření různých grafických kreací v závislosti na rytmu příp. frekvenci přehrávaných zvuků.

No a teď to dáte dohromady a máte plnohodnotný audio přehravač.

A k čemu se u tohoto příkladu otravovat s pluginy, když bez nich by to bylo mnohem jednodušší?

Jistě víte, jaký bouřlivý je vývoj různých možných i nemožných kodeků pro ukládání hudby (videa, obrázků,...).

Pokud byste celou aplikaci řešil bez technologie zásuvných modulů, musel byste okamžitě při vývoji celé aplikace do ní vložit i funkčnost pro všechny kodeky, které byste chtěl, aby to Vaše udělátko umělo přehrávat (wav, wma, mp3,....) Současně byste musel přehravač dovybavit různými vizuálními efekty, aby to bylo pro uživatele zajímavé (jako je třeba vizualizace ve WMP).

Je jasné, že čím budete chtít mít program univerzálnější a variabilnější, tím déle Vám vývoj celé aplikace bude trvat.

Až bude vybavena dost, tak to zkompilujete, odladíte, publikujete a odešlete k uživatelům.

Jenomže za čas se i těch Vašich 10 vizuálních efektů, které jste do aplikace poctivě nasázel, okouká, za nějaký čas za Vámi přijde kamarád s tím, že by si tímto prográmkem chtěl přehrát svou .midi skladbu, na kterou jste při tvorbě nepomyslel, nebo svět příjde s novým přelomovým kodekem, na který jste ani pomyslet nemohl.

No a Vám nezbyde, než otevřít celý projekt a doplnit do něj požadovanou funkčnost, znovu zkompilovat, odladit, publikovat a expedovat.

Ve chvíli, kdy využíváte zásuvné moduly, Vám stačí dodělat jenom další dll knihovnu s novými grafickými efekty případně kodeky, a kdokoliv si tuto knihovnu ke svému programu nahraje, může využívat rozšířených dovedností. (aniž by se jakkoliv muselo zasahovat do toho hlavního programu).

Nebo dokonce můžete zveřejnit a distribuovat Vámi navržené rozhranní (což není nic jiného, než popis jak má který konkrétní plugin vypadat, co a v jakém formátu má vyžadovat jako vstupy a co a v jakém formátu má vracet) a komunita uživatelů může jednotlivé plug-iny vyvíjet spolu s Vámi a distribuovat je ostatním uživatelům.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Prominte,asi jem se nevyjádřil dobře,a tak se i omlouvám že jste byl mou "donucen" napsat tento dlouhý příspěvek,který mne ovšem zase něco osvětlil,ale mne opravdu nešlo o to udělat prázdný formulář a časem na něj sázet pomocí knihoven funkce atd... Šlo mi o to co jistě znáte z windovs na pravém tlačítku myši před a po instalaci třeba WinRaru.On přeci doplní do nabídky další možnosti jako je "extrahovat..." a tak dale. Ale dobrá jeden konkrétnější příklad (to s Mp3 nebylo koukám příliš vhodné přirovnání).Budu mít program který bude umět spoustu věcí,ale jak říkáte,přijde den kdy zjistim že toho umí málo,přidám tedy knihovny.Já ale nevim jak je použít (myslim že jsem to psal v předchozích příspěcích),přidám novou (třeba matematickou) funkci ale z příkladu pana Linharta mi není jasné jak ty knihovny využiji.Najdu je,načtu je ale nevim jak se na jejich funkci odvola (pokud sem poslední kdo to nepochopil tak se omlouvám za svou tvrdohlavost). Děkuji.

ps: stejně si myslim že by ste na články vědomostně měl,ovšem chápu že i časová stránka se musí brát v potaz a toho času to nejspíš sežere hodně...

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Dejte mi ještě 15 minut!!!

Jestli neshodím místní server, tak to mám pro Vás už skoro připravené!

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Ještě než se pustím do toho pokusu "shodit server", tak mne napadla douška k tomu Vašemu textu - opět jste nezvolil nejlepší příklad, to, že po instalaci programu se Vám rozšíří kontextová nabídka (pod pravým tlačítkem myši), to s plug-iny nemá vůbec nic společného - to si jen program při instalaci zapsal nějaké informace na nějaké místo registru, které tuto nabídku obhospodařuje. Taky se to tady v diskusi již kdysi řešilo.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Diskuse: Jak rozšířit aplikaci o zásuvné moduly (Plug-in)

V čerstvě stažené verzi MVB 2008 na win XP příklad bez větších problémů funguje. Automaticky nabízená konverze proběhla bez chyb a pro korektní běh programu pak již stačilo nakopírovat obě zmiňované dll knihovny do příslušného bin adresáře projektu.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Jak rozšířit aplikaci o zásuvné moduly (Plug-in)

Zdravím Vás a měl bych leště jednu drobnou otázku:

Lze napojením Plug-inu nějak zasáhnout do definice výčtových typů hlavní aplikace?

O co mi jde.

Mám dnes nějakou aplikaci, která se, mimo jiné, zabývá animacemi fotek na screenu (přesněji různými přechody dvou fotek při slideshow). Takže ideální námět pro řešení pomocí Plug-inů.

Dnes to mám řešeno tak, že v rámci projektu mám definován enumerátor jednotlivých dostupných přechodů:

Public Enum prechod                                                 ' definice typu přechodu
    random = 0
    fade = 1
    shift_0 = 2
    shift_45 = 3
    ...
    ...
    throw_270 = 18
    throw_315 = 19
End Enum

protože předpokládám, že až budu mít ostatní funkčnost aplikace z krku, tak se budu bavit doděláváním dalších přechodů.

V rámci aplikace pak z dostupných přechodů vybírám nějaký konkrétní a ten aplikuji na přechod dvou fotografií.

Lze v rámci načtení nového Plug-inu (s novými přechody) nějak rozšířit tento výčtový typ, nebo bych místo něj měl použít třeba kolekci a do té ty nové možnosti načítat?

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Přesně tak, udržovat někde seznam dostupných pluginů by bylo ideální, do budoucna flexibilní řešení.

nahlásit spamnahlásit spam 2 / 2 odpovědětodpovědět

Když mi napíšete k jaké chybě u vás dochází, rád vám poradím.Bohužel po překopírování do Form1 mi to podtrhává slova IMathPlugin,DirectoryInfo,SearchOption,IMathPlugin. A já,ač je to hamba je neumim nahradit,nevim čím,mužete mi prosím říct kam se má přesně tato procedura vložit do formuláře a co musim upravit?(přesněji možná,jak to upravit)

Aby sem zbytečně neplašil a přitom to nebylo potřeba,píši totiž aplikaci na češtinu (už skoro rok) a konečně jsem se tomu dostal na kobilku,program rozpozná větu přací a podmětnou atd... ale Plug-in bych chtěl použít k dodatečnému vložení podmínek a třeba i s tim slovem ke kterému se vztahují.Jelikož je čeština velmi složitá a já nejsem zrovna češtinářsky zdatný,tak chci vkládat další možnosti aš časem... Otázkou je zda je to vhodná využití,ale to již ponechám na aplikaci.

Předem děkuji.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Odpusťte mi, ale nedá mi to, než si ze začátku trošičku "píchnout". Dost hodně mě pobavilo, že jste se pustil do aplikace na češtinu, a už jste se jí dostal na "kobilku". Ještě jednou se omlouvám, protože vím, že tyto stránky jsou o jiném jazyku než o češtině, ale v této kombinaci to působí opravdu vesele.

Ale k Vašemu dotazu.

Podle toho, co píšete, že Vám to podtrhává to vypadá, že vám chybí v referencích odkaz na dll knihovnu "plugininterface", a dále že jste v projektu opomněl natažení jednotlivých jmenných prostorů, nebo jste je dal na špatné místo (musí být před třídou Formx, ne uvnitř) - to jsou ty řádky

imports System.Collections.Generic
Imports System.IO
Imports System.Reflection

Jinak si přečtěte, jakou chybu Vám to u toho podtržení vypisuje. Pokud je tam něco ve stylu "Type "abcd" is not defined", tak to většinou svědčí o tom, že nemáte nainportován nějaký jmenný prostor.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Chyba kterou píšete je způsobena tím, že nemáte přidanou referenci na knihovnu PluginsInterface.dll, dále nemáte naimportován jmenný prostor OL.SimplePluginsExample a System.IO. Myslím že byste měl nejdřív nastudovat základy než se pouštět do něčeho složitějšího.

Mimochodem modul pro korekturu české gramatiky (který byl vyvíjen několik let ve spolupráci s Ústavem pro jazyk český) existuje již dlouho pro Microsoft Office 2003 a dá se použít pomocí COM rozhraní i ve vaší aplikaci.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Je krásné sice že je již napsán ale nemyslim si že bych se potom něco naučil a nebo že by sem to uměl použít tak jak potřebuji.

A základy nastudované mám ovšem učím se jen z tohoto webu a toto sou myslim první zmínky na toto téma zde...

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Tak bohužel,jmenne prostory importované mám,zkontrolováno jak jste napsali ale stále to podtrhává to same,Prosím kam mám přesně kod umístit?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

A kde jej máte dnes? A co Vám to vypisuje za chyby u těch podtržených výrazů?

Když tak to zkusím večer, ale tak z voleje - Ty pluginy by měly zůstat asi tak jak jsou (ty dodávají pouze nějakou funkčnost, takže s Formem nemají až tak moc společného.

No a u toho hlavního modulu-

Importy by měly být úplně na začátku, ještě před definicí Class Form, no a to, co je v proceduře Main by chtělo rozdělit.

Ty deklarační věci, načtení PLUG-inů apod. by bylo asi nejlépe umístit do metody Form1_Load,

to co je součástí testů těch Pluginů (asi od

If plugins.Count > 0 Then

byste si měl dát někde dle potřeby - pro testování třeba pod nějaké tlačítko.

Ještě nezapomeňte na správné deklarace proměnných - nemám to teď moc času dopodrobna procházet, ale minimálně pluigins by měla být deklarována jako globální proměnná.

SPíše bych Vám ale doporučil, projděte si pozorně celý příklad a pokuste se přijít na to, co který řádek dělá. No a pak už určitě příjdete na to i sám, kam nejlépe jednotlivé části kódu umístit.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback