GeoGet

Complete geocaching solutions

User Tools

Site Tools


user:skripty:dlls

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
user:skripty:dlls [2016/09/07 07:37] – [Konvence volání funkcí] gorduser:skripty:dlls [Unknown date] (current) – external edit (Unknown date) 127.0.0.1
Line 49: Line 49:
 end; end;
 </code> </code>
 +
 +
  
 ====== Vytvoření DLL knihovny pro použití v GeoGetu ====== ====== Vytvoření DLL knihovny pro použití v GeoGetu ======
 +
 Vytvoření knihovny v Delphi asi nebude dělat velké problémy, ale pro knihovnu vytvářenou v C++ je třeba si uvědomit odlišnosti mezi Delphi a C++. Jde zejména: Vytvoření knihovny v Delphi asi nebude dělat velké problémy, ale pro knihovnu vytvářenou v C++ je třeba si uvědomit odlišnosti mezi Delphi a C++. Jde zejména:
  
-  * o konvenci volání funkcí 
   * o dekorování jmen funkcí   * o dekorování jmen funkcí
 +  * o konvenci volání funkcí
  
 Demonstrovat řešení budu na Microsoft Visual Studiu (já konkretně mám verzi 2010). U jiných verzí MSVC to bude podobné a analogie se najde jistě u jiných kompilátorů - když se ví, co hledat, je to jednodušší. Demonstrovat řešení budu na Microsoft Visual Studiu (já konkretně mám verzi 2010). U jiných verzí MSVC to bude podobné a analogie se najde jistě u jiných kompilátorů - když se ví, co hledat, je to jednodušší.
 +
 +<WRAP tip>Pro použití knihovny je nutné, aby byla uložená tam, kde ji bude systém hledat, tedy na jednom z těchto míst:
 +
 +  - v některém z vhodných systémových adresářů (např \Windows\SysWOW64)
 +  - v adresáři, který je uveden v systémové proměnné PATH (samozřejmě je možné tuto proměnnou upravit)
 +  - v adresáři, kde je exe soubor, který knihovnu používá (v našem případě GeoGet.exe)
 +  - v aktualním (pracovním) adresáři, zde bz to byl DATADIR
 +  - upravit impor funkce a doplnit jméno knihovny o relativní cestu (vůči DATADIR) k ní, např.
 +    ''function SoucetStr(s1,s2:PChar):PChar; external 'SoucetStr@.\script\jmeno_meho_pluginu\MojeKnihovna.dll cdecl delayload';''
 +
 +Doporučoval bych některou z posledních tří možností.
 +</WRAP>
 +
 +===== Dekorování jmen funkcí =====
 +Standardně C/C++ používá pro jména funkcí tzv. zdobení mebo dekorování. To znamená, že jméno funkce je obaleno nějakými znaky, které mj. linkeru slouží pro kontrolu typu funkce a počtu parametrů. Protože Delphi žádné zdobení nepoužívá, musíme zajistit, aby jména funkcí v knihovně byla shodná s tím, jak je máme pojmenované ve zdrojovém kódu. K tomu slouží DEF soubor použitý při linkování.
 +
 +Napřed tedy vytvoříme soubor ''MojeKnihovna.def'':
 +
 +<code>
 +LIBRARY   MojeKnihovna
 +EXPORTS
 +    SoucetInt  @1
 +    SoucetStr  @2 
 +</code>
 +
 +a přikážeme jeho použití: ''Configuration Properties --> Linker --> Input --> Module Definition File'', kde nastavíme hodnotu ''$(ProjectDir)\MojeKnihovna.def''
 +
 +Pochopitelně je potřeba také funkce správně exportovat, např.
 +
 +<code C++>
 +
 +#define LIB_EXPORT __declspec(dllexport) 
 +#define LIB_IMPORT __declspec(dllimport) 
 +
 +LIB_EXPORT LPCSTR SoucetStr(LPCSTR s1, LPCSTR s2){ 
 + CString sa,sb;
 + sa=CString(s1); sb=CString(s2);
 + sa=sa+sb;
 + return(sa);
 +};
 +
 +LIB_EXPORT int SoucetInt(int i1,int i2){ 
 + return(i1+i2);
 +};
 +</code>
 +
 +Pokud by mely být funkce použité ve vlastní aplikaci C/C++, musí být naopak z knihovny správně importované:
 +
 +<code C++>
 +
 +LIB_IMPORT LPCSTR SoucetStr(LPCSTR s1, LPCSTR s2);
 +LIB_IMPORT int SoucetInt(int i1,int i2);
 +</code>
 +
 +
  
 ===== Konvence volání funkcí ===== ===== Konvence volání funkcí =====
 Když máme správně pojmenované funkce v knihovně, můžeme se vrhnout na jejich volání. Když máme správně pojmenované funkce v knihovně, můžeme se vrhnout na jejich volání.
-Implicitní volaní v C/C++ je typu ''cdecl'', zatimco v Delphi je to ''stdcall''. Je tedy potřeba uvést do souladu knihovnu s pluginem. Jsou 2 cesty:+Implicitní volání v C/C++ je typu ''cdecl'', zatimco v Delphi je to ''stdcall''. Je tedy potřeba uvést do souladu knihovnu s pluginem. Jsou 2 cesty:
  
   * přizpůsobit plugin   * přizpůsobit plugin
Line 76: Line 134:
 Pro přizpůsobení knihovny je třeba změnit implicitní nastavení kompilátoru: Pro přizpůsobení knihovny je třeba změnit implicitní nastavení kompilátoru:
  
-**Configuration Properties --> C/C++ --> Advanced --> Calling Convetion** na hodnotu ''__stdcall (/Gz)''+''Configuration Properties --> C/C++ --> Advanced --> Calling Convetion'' na hodnotu ''__stdcall (/Gz)''
      
 a v souladu s ním deklarovat funkce v pluginu: a v souladu s ním deklarovat funkce v pluginu:
Line 86: Line 144:
  
 Pokud takto změníte volací konvenci a knihovnu použijete také ve své vlastní aplikaci vytvořené v C/C++, je na to třeba pamatovat a knihovní funkce použít správně. Pokud takto změníte volací konvenci a knihovnu použijete také ve své vlastní aplikaci vytvořené v C/C++, je na to třeba pamatovat a knihovní funkce použít správně.
-===== Dekorování jmen funkcí ===== 
  
 +
 +
 +===== Přebírání parametru typu string z DLL knihovny =====
 +
 +<WRAP important>U návratových hodnot typu ''string'' (''ansistring'') je problém s uvolněním paměti při ukončení pluginu. Zřejmě se nějak pere správa paměti v Geogetu a ve scriptengine. Vhodné řešení spočívá v úpravě knihovních funkcí a jejich volání tak, aby ve skutečnosti nebyl vracen typ ''string'', ale nejaký ordinarní typ proměnné.</WRAP>
 +
 +  * knihovní funkce má navíc 2 parametry: adresu textového bufferu a jeho alokovanou velikost
 +  * místo aby funkce vracela string, zápíše string do bufferu a vrátí počet zapsaných znaků
 +
 +Takže s ohledem na výše řečené bude plugin obsahovat něco takovéhoto:
 +
 +<code delphi>
 +//deklarace knihovní funkce
 +function SoucetStr(s1,s2,sBuffer: PChar; iMaxLen:integer): integer; external 'SoucetStr@MojeKnihovna.dll stdcall delayload';
 +...
 +
 +//volání a převzetí vráceného textu
 +const MAX_BUFR_LENGTH=256;
 +
 +var sBuffer:ansistring; // !! pokud pouzijete string misto ansistring, 
 +                        // !! bude na konci sBuffer obsahovat prazdny text, 
 +                        // !! protoze 1. byt znaku bude mit hodnotu 0
 +                        // !! a to bude chapano jako konec textu 
 +    iLen:integer;
 +begin
 +  ...
 +  SetLength(sBuffer,MAX_BUFR_LENGTH);
 +  iLen:=SoucetStr('12345','abcde',sBuffer,MAX_BUFR_LENGTH);
 +  SetLength(sBuffer,iLen);
 +  ...
 +end;
 +</code>
 +
 +a funkce v DLL knihovně může vypadat třeba nějak takto:
 +
 +<code C++>
 +LIB_EXPORT int SoucetStr(LPCSTR s1, LPCSTR s2, LPSTR sBuffer, int iMaxLen){
 +  CString cs=CString(s1)+CString(s2);
 +  strcpy_s(sBuffer,iMaxLen,cs);
 +  return(strlen(sBuffer));
 +}
 +</code>
user/skripty/dlls.1473226668.txt.gz · Last modified: 2016/09/07 00:00 (external edit)