DLL irása Delphiben : Mik a szabvány típusok
2006-10-17T14:24:04+02:00
2008-07-04T10:44:22+02:00
2022-07-26T18:21:23+02:00
E_Pluribus_Unum
Üdv!

DLL-t fejlesztek Delphi7-ben, de a DLL-t a többi programozási nyelvi is meghívná, ehhez viszont "windows standard típusokat" kell használnom a paraméterlistában. A kérdésem, hogy mik azok a "windows standard típusok" és hogyan kell definiálni őket Delphi7-ben.

Ami egyértelmű és használható :
- Integer
- PChar
- rekordoknál a packed kulcsóval együtt

Ami érdekelne :
- Byte, Word, LongWord, ShortInt, SmallInt, Int64, Currency, Single, Double
- PAnsiChar, PWideChar
- TDateTime,
- Pointer (ha jól tudom nincs minden nyelben)
- Variant
- dinamikus tömbök

Ami tudom hogy nem az :
- Extended
Mutasd a teljes hozzászólást!
Biztosan használható típusok (más nyelvi megfelelővel):
Delphi C++ méret előjel ---------------------------------------------- Byte byte (vagy char) 1 byte nincs Word unsigned short 2 byte nincs LongWord unsigned long 4 byte nincs LongInt signed long 4 byte van SmallInt signed short 2 byte van Single float 4 byte van Double double 8 byte van

A PAnsiChar és a PWideChar egy speciális mutató típus ami a PChar-hoz hasonlít. Elvileg használatuk lehetséges, de nem nagyon ajánlom, és eddigi tapasztalataim szerint teljes mértékben kiválhatóak a PChar-ral.

A TDateTime 8 byte-os típus. Definíció szerint:
Type TDateTime = Type Double;
Szóval ezt is használhatod kimentnek, csak akkor a fogadó programnak, ha nem Borland termék akkor konverziót kell végrehajtani vagyis a double típusból kinyerni az időt.

Pointer: Ez elég erősen típus függő. Szinte minden nyelvebn megtalálható. Biztosan használhatod (hisz pl. a PChar is pointer és azt is használod) csak ilyenkor elég terjedelmes manualt kell majd mellékelned a DLL-hez amiben leírod, hogy pointer kimenet mekkora méretű memória területre mutat, és azon milyen elemi típusok helyezkednek el.

Variant, ne használd. Ez csak a Delphi speciális változója.

Dinamikus tömb. Ezt se használd. Inkább itt is marad a jól bevált pointeres módszer. Az eljárásod kiemenete legyen egy pointer a dinamikus tömb kezdőcímére és itt is add meg, hogy mekkora méretű egy-egy tömb elem. Így a DLL-t használó progi már tudni fogja, hogy hol találja az adott sorszámú elemet, amit a pointer és a sorszám kombinációjából kapott címről ki is tud olvasni.
Mutasd a teljes hozzászólást!

  • Nem tudom, hogy a 'Windows standard típusok' létezhet-e mindenféle nyelvben, de nekem ilyen helyzetben - azaz dll-ben fg., több nyelvből hívva - jól működött, hogy az összetettebb adatokat (pl. tömbök)szöveges formában - a'la szövegfájlok - adtam/vettem, azaz a fogadott paraméterek a 'biztosan' működő pchar-ra (vagy azzal kompatibilsre) voltak alapozva (vagy más esetben a clipboard-ot is lehetett erre használni - és ezzel még az adatátvitelt is viszonylag könnyen lehetett debug-olni).
    Összetett adatszerkezetkenél érdemesebb visszanyúlni nagyon alap dolgokhoz, mert még azonos programnyelven is érhetik meglepetések az embert - mivel a klf. modulok (a hívók és a dll-ek) nem feltétlenül azonos fordítási eredményt adnak, csak magukban konzisztensek a hivatkozások/adathatárok.
    Mutasd a teljes hozzászólást!
  • Szerintem (normális kereteken belül) használhatod az összes változót, mivel valamilyen megfelelője biztos van a másik nyelvben is. PL az OpenGL32.dll-ben is található rengeteg C++ szabvány típus, mégis van 'Delphiesített' változata.

    Szvsz használhatod:
    byte, longword, shortint, smallint, currency,single, double, pointer (?),
    PAnsiChar, PWideChar
    Mutasd a teljes hozzászólást!
  • Ugye milyen ostoba kis problémák áthidalására képes a .NET keretrendszer . Ezt csak úgy megemlítem.

    Egyébként elvileg igen, a Windows natív adattípusokat nyugodtan használhatot. Akkor kezdődik a gond, ha egy Delphi objektumot akarsz átadni. Ebben az esetben csak a VCL eszközök alatt (Delphi, C++Builder...) lesz használható az adott függvény. Ráadásul ott is a VCL verziójától függő lesz. Hála Istennek, hogy megalkották a .NET keretrendszert, ami többek között megoldást jelent erre a problémára is, csak sajna Delphi alatt még csak 1.1 .NET van. Mondjuk a napokban jött ki a Delphi Highlander Beta .NET 2.0 CLR-el.
    Mutasd a teljes hozzászólást!
  • Nos, ha tömböt vagy stringet akarsz használni, akkor - legalábbis delphi alatt - definiálni kell a ShareMem unitot a uses sorban, és a dll-t használó programban (vagyis a .dpr-ben). Lényeg h mindkét helyen ez legyen az első unit amit definiálsz. Delphiben ez létszükség, ahogy erre fel is híják a figyelmed a generált alap dll kódban. Azt nem tudom, hogy más programozási nyelvek, vagyis azok fordítói hogyan küszöbölik ki ezt a hibát. Ha szabad azt még megjegyeznem, hogy az exports résznél ha kellene ha nem... fejtsd ki minden fejlécet rendesen. Ellenkező esetben még az alap alkalmazások sem ismerik fel a dll-ben a függvényeket, mert azt mondaná, h más a verziószáma - holott ugyanaz. Hála a windows által megteremtett dll hell-nek.
    Mutasd a teljes hozzászólást!
  • Köszönöm, eddig te segítettél a legtöbbet.
    Fontos lenne még a dinamikus tömb és a dátum. A pointerekhez meg megerősítés kellene, kissé bizonytalanná tesz a kérdőjel.
    Most ezek az aktuálisak a Variant egyenlőre nem izgat, nem hinném hogy a közeljövőben használnám, az objektumokról meg edig is tudtam hogy borland specifikus. A sharememről is tudtam elvégre a unit fejléce is írja, mint ahogy írta valaki.
    Mutasd a teljes hozzászólást!
  • Úgy érted
    interface ... function GetUgyfel(Conn : PChar;ID : PInteger) : PUgyfel; stdcall; function GetUgyfelek(Conn : PChar;Mezo : PChar;Ertek : PChar) : PUgyfelek; stdcall; ... exports GetUgyfel, GetUgyfelek, ...

    helyett mást/többet kellene írni? Ez nem elég? Delphiben ez elegendő az elkészült dll használatához.
    Mutasd a teljes hozzászólást!
  • Nézd meg a Filesystem Dialogs Library .dll-em!
    Jó sokmindenre találsz példát benne. Ez egy ShareMem nélküli .dll, tehát példa arra, hogy lehet univerzális .dll-t írni Delphi alól.
    Van példa a szerintem helyesebb exports rész használatra is benne (const-tal definiálás, stb.).
    Mutasd a teljes hozzászólást!
  • Üdv! Valóban van benne egy/két érdekes dolog, köszönöm. Látom hogy a pointer használható, használható a set is (ami eddig teljesen el lett felejtve), és ami érdekes lehet function/procedure típus is (bár asszem ez a pointerből egyértelműen adódik).

    Továbbra is kérdéses a TDateTime és a dinamikus tömbök kompatibilitása a többi nyelvvel.
    Mutasd a teljes hozzászólást!
  • Biztosan használható típusok (más nyelvi megfelelővel):
    Delphi C++ méret előjel ---------------------------------------------- Byte byte (vagy char) 1 byte nincs Word unsigned short 2 byte nincs LongWord unsigned long 4 byte nincs LongInt signed long 4 byte van SmallInt signed short 2 byte van Single float 4 byte van Double double 8 byte van

    A PAnsiChar és a PWideChar egy speciális mutató típus ami a PChar-hoz hasonlít. Elvileg használatuk lehetséges, de nem nagyon ajánlom, és eddigi tapasztalataim szerint teljes mértékben kiválhatóak a PChar-ral.

    A TDateTime 8 byte-os típus. Definíció szerint:
    Type TDateTime = Type Double;
    Szóval ezt is használhatod kimentnek, csak akkor a fogadó programnak, ha nem Borland termék akkor konverziót kell végrehajtani vagyis a double típusból kinyerni az időt.

    Pointer: Ez elég erősen típus függő. Szinte minden nyelvebn megtalálható. Biztosan használhatod (hisz pl. a PChar is pointer és azt is használod) csak ilyenkor elég terjedelmes manualt kell majd mellékelned a DLL-hez amiben leírod, hogy pointer kimenet mekkora méretű memória területre mutat, és azon milyen elemi típusok helyezkednek el.

    Variant, ne használd. Ez csak a Delphi speciális változója.

    Dinamikus tömb. Ezt se használd. Inkább itt is marad a jól bevált pointeres módszer. Az eljárásod kiemenete legyen egy pointer a dinamikus tömb kezdőcímére és itt is add meg, hogy mekkora méretű egy-egy tömb elem. Így a DLL-t használó progi már tudni fogja, hogy hol találja az adott sorszámú elemet, amit a pointer és a sorszám kombinációjából kapott címről ki is tud olvasni.
    Mutasd a teljes hozzászólást!
  • cső

    igen az exportsot fejtsd ki bővebben azzal csak nyerhetsz. tehát ird oda milyen paramétereket várnak. A plugin rendszereknél kötelező h az exprots rész pontról pontra megegyezzen az előzővel, másképpnem űködnének a progik. hála a windowsnak. Ami még érdekes: ha csinálsz két progit ami 1 dll-t használ, és ha azokat telepíted(vagyis mindkét progi mellé felrakod) akkor a windows 2 dll-t fog érzékelni, nem egyet. windows alatt erre az embeernek kell odafigyelni, megoldani nemtom h hogy kell. Lényeg ha több dll vann fent aminek ugyanaz a verziója akkor már megint a dll hellnél tartunk. képtelen helyesen kezelni a telepített DLL-eket. amugy erről itt olvashatsz: DLL hell - Wikipédia
    az angol megfelelőjén talánmég többet is irnak.

    Mint ahogy már barnaa is irta ne használj se stringeket se tömböket, bármilyen legyen is az. Sztem pointereket bűn lenne nem kezelni valamelyik nyelvben, de ettől még lehet h van ilyen.
    Mutasd a teljes hozzászólást!
  • Na ez egy szép összegzés. Ezek szerint nincs szabvány dátum, mindenkinek magának kell gondoskodni a kompatibilitásról. A Variant miatt nem fáj a fejem nem volt betervezve, csak ha már belekezdtem legyen összegezve az összes típus/adatstruktúra. Ami viszont érvágás az a dinamikus tömb, nyílvánt több módon is lehet pótolni*, de egyik sem az igazi.

    - Láncolt lista (ezzel kissé bonyolultabb az adatszerkezet és a kezeléséhez plusz eljárások szükségesek)
    - Alacsony szintű memóriakezelés (nem hinném hogy ezt minden nyelv tudja, a pointer megléte még nem elegendő ahhoz hogy korrekt módon lehessen operálni vele)
    - Mindent stringbe szeparáló karakterekkel (talán ez a legfájdalommentesebb de itt is kellenek plusz eljárások)

    Egyéb lehetőségek?
    Mutasd a teljes hozzászólást!
  • Köszi.

    Nem vagyok Visual Basic 6 guru, de asszem abban nincs Pointer, de javítsatok ki ha tévedek.
    Mutasd a teljes hozzászólást!
  • A String elválasztó karakterekkel a legbonyolultabb!
    Marad az "alacsony szintű" memóriakezelés, mert ezt szinte minden nyelv tudja:
    Pascal, Delphi, C, C++, C# (unsafe kódban).

    ( u.i.: Ha igazán alacsonyan akarsz memóriát kezelni akkor ASM )


    A dinamikus tömbözés lényege:

    PÉLDA
    1.)A DLL eljárása:
    Function DinamikusTombAVege :Pointer;

    2.)A manualba ez van hozzá írva:
    A kiemenete egy dinamikus tomb, melynek elemei 4 byte-os LongInt-ek, az elemek sor-folytonosan helyezkednek el a memóriában. Az pointer utáni első 4 byte egy LongWord ami az elemek darabszámát adaja.


    3.)A kiementi memória "kinézete":
    kezdőcím pointer tömb vége | | V V |elemszám | adat | adat |.... |

    4.)Beolvasása Delphiben (persze bármi másba meg lehet írni):
    Procedure Feltolt(p :Pointer); Var dintomb :Array of LongInt; N :LongWord; i :Integer; item :Pointer; step :LongInt; Begin N:=LongWord(p^); //elemszám lekérés SetLength(dintomb,N); //tomb létrehozás step:=SizeOf(LongInt); //lépésköz a memóriában (LongInt mérete) item:=Inc(p,step); //az első tömbelemre állunk For i:=0 to N-1 Do Begin dintomb[i]:=LongInt(item^); //i-edik elem olvasása item:=Inc(item,step); //lépés a memóriában End; End;


    Persze ez a módszer magában hordozza, azt a veszélyt, hogy mi van, ha a darabszámot rosszul olvassuk ki, vagy túlfut az olvasó ciklus (emiatt unsafe a kód).De ezekre egyszerűen vigyázni kell!
    Mutasd a teljes hozzászólást!
  • Ez mind szép, de ha a túloldal nem tud írni, akkor nem ér semmit. Konkrétan jelenleg két nyelv érdekel, a többi csak mint jövőbeli lehetőségek miatt szükséges :
    - Visual Basic 6
    - Progression
    Mutasd a teljes hozzászólást!
  • Mint ahogy már barnaa is irta ne használj se stringeket se tömböket, bármilyen legyen is az.


    Tudom javasolni, hogy belsőleg konvertáljátok át Delphi-s Stringre őket, és a végén alakítsátok vissza. Ez kb két-három sor, egy sima CopyMemory(PChar, Length). A Delphi-s Stringeket sokkal könyjebb kezelni.

    Érdekes ez a DLL hell. Szerintem úgy kell megoldani, hogy verziózva van, vagyis:

    Funkcio_1_0(Param: Word); Funkcio_1_1(Param: Word);

    ezt aztán update-elt include file-okkal (épp a const-os megoldás amit említettem) mindig a legújabbat lehet használni, és a régi is mindig benne marad fizikailag a .dll-ben, így futnak a régi programok is vele.
    Mutasd a teljes hozzászólást!
  • Ha visszatekeritek az idő kerekét, akkor észrevehetitek, hogy Amigán, úgy működik, hogy:

    OpenLibrary(STRPTR LibName, ULONG Version)

    Nem lehetne úgy tárolni a .dll-eket, hogy a Win verzió alapján töltse be őket?
    Amigán ha 0-át adunk át,
    akkor bármilyen verziószámot talált is a könyvtárban, megnyitja azt
    .
    Mutasd a teljes hozzászólást!
  • Sokmindent takarhat a "használni" szó (string, rekord, tömb):

    1.paraméterként átvenni
    2.eredményként átadni
    3.olvasni a tartalmát
    4.írni bele
    5.létrehozni és felszabadítani
    6.átméretezni

    Ebből egy része működhet (a Windows függvények is intenzíven használnak ilyet), bizonyos dolgok viszont nem. Tehát

    1.nyugodtan lehet, csak STRING-et PCHAR-ként; string és tömb esetén kell egy másik paraméter a méretnek (rekordnál vagy ismeri mindenki a pontos szerkezetet és méretet, vagy a rekord első elemében át szokták adni és onnantól verziófüggetlen tud lenni a függvény).

    2.ha paraméterként átadott memóriára mutat, akkor rendben (pl. tömbkeresés kap egy tömböt paraméterként és visszaadja a találat helyét), de egy lokálisan létrehozott területet (pl. lokális stringet kiadni) TILOS! Pl. ez rossz:

    function Valami : pChar;
    var
    s : array[0..10] of char;
    begin
    s:='Hello!';
    Result:=s;
    end;

    de ez jó lehet:

    function Valami : pChar;
    begin
    Result:='Hello!';
    end;

    Másik működő verzió, hogy szabvány Windows memóriakezeléssel (GlobalAlloc) hozod létre az eredményt, amit a függvény felhívójának azután kötelessége felszabadítani.

    3. és 4. működik; írni addig szabad, amíg biztos nem nyúlik túl a méreten!

    5. és 6. a Delphi memóriakezelése csakis Delphi alatt működik (erre kell a ShareMem unit), de ha vállalod a GlobalAlloc és társai által kínált op.rendszer-féle memóriakezelést, akkor gyakorlatilag bármit megtehetsz; csak pontosan tudni kell a hívónak és a DLL-nek is, hogy ki mit csinál. Ez azért elég bonyi szokott lenni, egyszerűbb, és biztonságosabb, ha a hívó kezeli a memóriát, a DLL abból dolgozik, amit kapott. Pl. gyakori, hogy nil pointerrel hívnak egy függvényt, mire nem végzi el a kívánt műveletet, hanem visszaadja az elvárt string/tömb/rekord méretet, erre újra fel lehet hívni, mostmár a szükséges méretű adattal.

    Pointer: valahol minden nyelvben van, hiszen memóriából élünk Visual Basic-ben pl. alapértelmezett a "ByRef" paraméterátadás, amikor valójában pointer a paraméter. Külső DLL hívásoknál is működik ez, a fentebb leírtakat figyelembe véve; bár lényegesen nehézkesebb pl. a GlobalAlloc használata és főleg az eredmény feldolgozása...
    Mutasd a teljes hozzászólást!
  • Ez valamogy kimaradt, de szerintem fontos a 'standard' dll-ben:
    cdecl
    Felteve ha a win dll-jeit standardnak tekintjuk.

    A dinamikus memoriafoglalasrol:
    Altalaban a hivo adja/foglalja a memoriateruletet, a dll pedig csak rapakolja az adatokat.
    Viszont pl COM-nal olyan is elofordul, hogy a dll foglal es csak egy pointert kuld vissza hivonak, amit az kesobb a dll CoTaskMemFree fuggvenyevel tud felszabaditani. Szal itt maga a dll valosit meg egy kis memoriamanagert, amit a hivo es a dll kozosen hasznal. Hasonlo a delphinel a sharemem unit, ami osszekoti a dll es a hivo delphis memoriamanagerét, igy kozosen hasznalhatnak mindenfele delphis dinamikus tipust, pl stringeket.
    Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd