Page 3 of 3 FirstFirst 1 2 3
Results 31 to 43 of 43

Thread: Geavanceerde Plugins

  1. #31
    mov rax,marcov; push rax marcov's Avatar
    Join Date
    Apr 2004
    Location
    Ehv, Nl
    Posts
    10,357
    Quote Originally Posted by GolezTrol View Post

    Overigens zijn WideStrings/BSTRs volgens mij juist niet reference counted. In tegenstelling tot de AnsiString implementatie van Delphi krijg je bij toekenning altijd een nieuwe string.
    Refcounting<>copy_on_write.

    Dynarrays zijn ook niet copy on write.

    Widestrings zijn vziw gerefcount in COM.

  2. #32
    Van http://wiki.freepascal.org/FPC_Unicode_support
    "widestring type on Windows targets remains non-refcounted and OLE compatible. Minimal number of helper routines is used for it. On non-Windows targets widestring is alias to utf16string."

    Van Chris Bensen
    "You are correct that WideString, aka BSTR, is not reference counted and is copied unless you pass it by var or const."

    Maar vooral de logica erachter: waarom zou je references gaan tellen? Als je bij toekenning altijd een nieuwe string krijgt, is de refcount dus altijd 1. Het hele nut van een telling is daarmee verdwenen. Referentie weg, string weg. Die reference counting is vooral van belang voor het copy-on-write principe, zodat je weet of je daadwerkelijk een kopie moet maken (als refcount > 1) of dat je de huidige string mag bewerken.
    1+1=b

  3. #33
    Reader
    Join Date
    May 2002
    Location
    Holland
    Posts
    3,382
    Widestring lijkt me inderdaad de beste manier voor cross-module strings.

  4. #34
    Senior Member
    Join Date
    May 2011
    Location
    Oisterwijk
    Posts
    468
    Quote Originally Posted by GolezTrol View Post
    Klopt. Maar volgens mij geldt dat dus expliciet niet voor WideString. Ik kan het mis hebben, hoor.
    Je hebt gelijk GolezTrol. Dat WideString niet refcounted is dat is nieuw voor mij.

    Quote Originally Posted by GolezTrol View Post
    Als je bij toekenning altijd een nieuwe string krijgt, is de refcount dus altijd 1. Het hele nut van een telling is daarmee verdwenen.
    Ja, maar. Je krijgt bij reference counting niet altijd een nieuwe string(instantie). Alleen als je een string veranderd (copy on write) gebeurt dat.

    In de documentatie bij String wordt COM zelfs genoemd bij WideString: (http://docwiki.embarcadero.com/RADSt...n/String_Types)
    The WideString type represents a dynamically allocated string of 16-bit Unicode characters. In some respects it is similar to AnsiString. On Win32, WideString is compatible with the COM BSTR type.
    WideString is appropriate for use in COM applications. However, WideString is not reference counted, and so UnicodeString is more flexible and efficient in other types of applications.
    Indexing of WideString multibyte strings is not reliable, since S[i] represents the ith element (not necessarily the ith character) in S.
    For Delphi, Char and PChar types are WideChar and PWideChar types, respectively.
    Dat is interresant marcov, misschien hoef ik een string niet als PWideChar te sturen, en kan een PWideString ook. Dan wordt de code weer wat logischer/vanzelfsprekender.

    (note to self: Meer over Widestrings en DLL's: http://stackoverflow.com/questions/9...using-sharemem )
    Last edited by Barry Staes; 14-Nov-12 at 13:45.

  5. #35
    mov rax,marcov; push rax marcov's Avatar
    Join Date
    Apr 2004
    Location
    Ehv, Nl
    Posts
    10,357
    Hmm. Ik dacht dat COM assignments ook simpelweg assignde (en dus refcount) (maar dan aan de COM kant), als ik de URLs in deze thread lees, is dat dus niet zo.

    De verwarring komt waarschijnlijk omdat FPC nog altijd refcounting naamgeving (als WIDESTR_INCR_REF) gebruikt in hulproutines, maar die routines lijken inderdaad te realloceren. Overigens ook op niet windows, dus ook daar is widestring anders dan unicodestring.

    En dat andere COM type, interfaces, zijn ook refcounted :-)

    Wel een verklaring waarom widestrings als "traag" gezien worden.
    Last edited by marcov; 14-Nov-12 at 14:51.

  6. #36
    Reader
    Join Date
    May 2002
    Location
    Holland
    Posts
    3,382
    En kunnen interfaces gebruikt worden tussen app en dll zonder ingewikkelde comconstructies? Dit is nieuw voor mij, dus misschien is dit een beginnersvraag

  7. #37
    mov rax,marcov; push rax marcov's Avatar
    Join Date
    Apr 2004
    Location
    Ehv, Nl
    Posts
    10,357
    Ik denk van wel. Alleen voor inter-process heb je per se COM nodig.

    Voor de gebruikte parameter types en calling conventies geldt uiteraard dezelfde beperkingen als gewone procedures.

  8. #38
    Ja hoor. Nou ja, ingewikkeld blijft het natuurlijk altijd een beetje, en je hebt wat beperkingen met de types, maar daar hebben we het al over gehad. Verder heb je maar een paar dingen nodig:

    - Een handvol interfaces om de dll en de applicatie met elkaar te laten praten.
    - Implementatie van die interfaces (in de app en in de plugin).
    - Een export functie die een interface teruggeeft.

    Die export is heel simpel:
    Delphi Code:
    1. function GetPlugin: IPlugin;
    2. begin
    3.   Result := TPlugin.Create;
    4. end;
    5.  
    6. exports
    7.   GetPlugin;
    Zoals je ziet geeft de functie een IPlugin terug. Er wordt feitelijk een TPlugin gemaakt, maar die implementeert de IPlugin interface.
    De eerste aanroep is dus altijd gewoon een DLL call.

    De interface kan er zo uit zien:
    Delphi Code:
    1. IPlugin = interface
    2.     procedure SetApplication(AApplication: IApplication);
    3.   end;
    Een plugin heeft dus een method om een applicatie aan door te geven. Op die manier kan de applicatie, nadat hij de DLL call heeft gedaan en de IPlugin heeft binnengekregen, zichzelf doorgeven aan de plugin. Zodoende kan de plugin dan ook methods van de applicatie aanroepen.

    Overigens gebruik ik wel netjes WideStrings, maar nog geen algemeen gebruikelijke calling convention. Wellicht handig om alle interface methods om te zetten naar stdcall.

    Ik wil dat de plugin in staat is om 'actions' te registeren in de applicatie. Een action is vergelijkbaar met een gewone TAction in een actionlist: hij heeft een caption (en eventuele andere properties) en kan uitgevoerd worden.

    Application heeft daarvoor een method nodig om zo'n action te registeren:
    Delphi Code:
    1. type
    2.   IApplication = interface
    3.     procedure AddAction(AAction: IAction);
    4.   end;
    Zo'n action ziet er zo uit:
    Delphi Code:
    1. type
    2.   // Een alias. Altijd handig voor als je nog eens van gedachten verandert over
    3.   // het juiste type.
    4.   PluginString = WideString;
    5.  
    6.   IAction = interface
    7.     function GetCaption: PluginString;
    8.     function Execute: PluginString;
    9.   end;
    De huidige flow is dus:
    - Application laadt DLL en roept de Export aan. Krijgt een IPlugin.
    - Application roept IPlugin.SetApplication(Self) aan. Plugin kent de applicatie.
    - Plugin roept Application.AddAction aan om een actie te registeren.

    Vervolgstappen:
    - Applicatie maakt een menu-item of button aan en koppelt deze aan de actie. D.w.z. als er op de button wordt geklikt wordt IAction.Execute aangeroepen.

    Life cycle management
    Bij de implementatie is het belangrijk om in de gaten te houden dat je met interfaces werkt. Interfaces zijn (meestal) reference counted. Dat wil zeggen dat het achterliggende object wordt vrijgegeven zodra er geen verwijzingen meer naar zijn.

    Dat kan in je voordeel werken. In dit geval zorg ik ervoor dat de applicatie altijd één verwijzing heeft naar zichzelf. De plugin wordt alleen bijgehouden door de applicatie in een lijst (TInterfaceList). Zodra je die lijst vrijgeeft, zijn de verwijzingen naar de plugin weg en zal het object zichzelf vrijgeven.
    Hetzelfde geldt voor de acties. De plugin hoeft deze allemaal niet bij te houden. Je geeft gewoon de interface door aan de applicatie en zodra deze er klaar mee is, gaat de rest redelijk vanzelf.

    Althans, het kost wel enkele tientallen regels code, natuurlijk. Ik een voorbeeldapplicatie geschreven met een simpele plugin. De plugin registreert één actie, waar de applicatie een menuitem voor maakt. Als je die activeert, toont de plugin een input box en geeft de ingetypte waarde terug aan de applicatie die hem weer toont in z'n statusbalk.

    De indeling spreekt denk ik voor zich:
    - 2 dpr's
    - fMain met applicatie form
    - uPluginInterface bevat de gedeelde interfaces (wordt gebruikt in beide projecten).
    - uPluginManager beheert de boel aan de kant van de applicatie
    - uPluginCode bevat de code van de plugin zelf.

    Bij elkaar nog geen 300 regels, inclusief al het auto-gegenereerde spul en het uitgebreide commentaar.

    Garanties
    Tot de deur. Het appje lijkt te werken, maar ik weet niet of ik nog stomme dingen over het hoofd zie. Ik heb ook niets geprofiled of getest om mem leaks.
    Het zou kunnen dat ik het interfaceverhaal zelf ook verkeerd begrepen en ik zwaar illegale dingen zit te te doen, maar volgens mij is het wel allemaal volgens de regels.
    Attached Files Attached Files
    1+1=b

  9. #39
    Reader
    Join Date
    May 2002
    Location
    Holland
    Posts
    3,382
    Ha leuk voorbeeld! Dank. En dat is dus taal onafhankelijk? Een DLL in VB of C of wat dan ook geschreven kan op dezelfde manier werken dus?
    Zolang ze maar op de een of andere manier een correcte declaratie van de interfaces hebben lijkt me.

  10. #40
    Dat zou inderdaad moeten kunnen. Maar houd dan wel rekening met die calling convention.
    1+1=b

  11. #41
    mov rax,marcov; push rax marcov's Avatar
    Join Date
    Apr 2004
    Location
    Ehv, Nl
    Posts
    10,357
    Quote Originally Posted by GolezTrol View Post
    Die export is heel simpel:
    Delphi Code:
    1. function GetPlugin: IPlugin;
    2. begin
    3.   Result := TPlugin.Create;
    4. end;
    5.  
    6. exports
    7.   GetPlugin;
    Export dat niet met gemanglede naam? Lastig aanroepen :-)

    Anyway, ik vraag me af of dit op Linux werkt. De verschillende getplugin functies zouden met elkaar clashen.

  12. #42
    Dll-aanroepen werken op Linux toch niet wezenlijk anders? Uitgaande van 1 plugin-object per plugin dll zou dit prima moeten werken. Om alle plugins te laden kun je simpelweg alle dlls in een specifiek mapje openen. Volgens mij is dat wel een gebruikelijke methode (zie o.a. Notepad++)
    1+1=b

  13. #43
    mov rax,marcov; push rax marcov's Avatar
    Join Date
    Apr 2004
    Location
    Ehv, Nl
    Posts
    10,357
    Quote Originally Posted by GolezTrol View Post
    Dll-aanroepen werken op Linux toch niet wezenlijk anders?
    Jawel. Op Linux roep je een symbool aan. Op Windows een (module, symbool) combinatie.

    Zie eerder in deze thread voor de details:
    http://www.nldelphi.com/forum/showpo...2&postcount=22

    Disclaimer: ik heb daar alleen een paar keer langs geaaid, niet echt uitgezocht (dat staat ook op de todo lijst), maar Linkers en Loaders (free ebook over linkers) lijkt me gelijk te geven.

    Zie verder ook de uitleg bij (GNU) linker parameter -Bsymbolic

Page 3 of 3 FirstFirst 1 2 3

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •