TCH (statz) | #1, Főfasz (10466) |
27596 | #5461 | ^ | Idézet | Sat, 30 Oct 2021 18:11:01 +02 |
84.236.*.* | *.pool.digikabel.hu |
Pluginezés UNIX alatt C és Pascal nyelven rovat. Gondoltam csinálok a témakörben egy tutorialt. Vágjunk bele. A pluginek tulajdonképpen kutyaközönséges dinamikus library-k, amiket a program futásidőben tud betölteni, vagy felszabadítani. Ennek megfelelően elsőként csináljunk egy dinamikus könyvtárat. Legyen kezdetnek mondjuk három függvényünk. C-ben (mylib.c): int mylib_a(int a) { return a - 5; } void mylib_b(int *b) { *b -= 5; } int mylib_c(int *c) { *c -= 5; return *c - 5; }És Pascalban (mylib.pas): library mylib; {$mode objfpc}{$H+} function mylib_a(a: longint): longint; cdecl; begin result := a - 5; end; procedure mylib_b(b: plongint); cdecl; begin b^ := b^ - 5; end; function mylib_c(c: plongint): longint; cdecl; begin c^ := c^ - 5; result := c^ - 5; end; exports mylib_a, mylib_b, mylib_c; end.A C library úgy áll elő, hogy először leforgatjuk relokálható (-fPIC kapcsoló) objektumnak (-c kapcsoló) cc -fPIC -c mylib.cmajd utána linkeljük Shared Object-nek (-shared kapcsoló, a -Wl,-soname,libmylib.so pedig a linkernek mondja meg, hogy mi lesz az object neve) cc -shared -Wl,-soname,libmylib.so -o libmylib.so mylib.oPascal alatt egyszerűbb a dolog; mivel a forrásban deklarálva van, hogy ez egy library, így elég csak simán a fpc mylib.pasparanccsal megküldeni. Ezzel előállt a library mindkét nyelven, benne a mylib_a, mylib_b, mylib_c symbolokkal; lehet ellenőrizni a objdump -T libmylib.soparanccsal. A kimenet a C-s esetén: libmylib.so: file format elf64-x86-64 DYNAMIC SYMBOL TABLE: 0000000000000000 w D *UND* 0000000000000000 _ITM_deregisterTMCloneTable 0000000000000000 w D *UND* 0000000000000000 __gmon_start__ 0000000000000000 w D *UND* 0000000000000000 _Jv_RegisterClasses 0000000000000000 w D *UND* 0000000000000000 _ITM_registerTMCloneTable 0000000000000000 w DF *UND* 0000000000000000 GLIBC_2.2.5 __cxa_finalize 0000000000201020 g D .data 0000000000000000 Base _edata 0000000000201028 g D .bss 0000000000000000 Base _end 0000000000000690 g DF .text 000000000000000f Base mylib_a 000000000000069f g DF .text 000000000000001a Base mylib_b 0000000000201020 g D .bss 0000000000000000 Base __bss_start 0000000000000550 g DF .init 0000000000000000 Base _init 00000000000006dc g DF .fini 0000000000000000 Base _fini 00000000000006b9 g DF .text 0000000000000022 Base mylib_cÉs a Pascalos esetén: libmylib.so: file format elf64-x86-64 DYNAMIC SYMBOL TABLE: 0000000000000a10 g DF .text 0000000000000005 mylib_a 0000000000000a20 g DF .text 0000000000000005 mylib_b 0000000000000a30 g DF .text 0000000000000005 mylib_cAmint látszik a C-s dinamikusan linkelődik a libc-hez, a míg a Pascalosnak nincs külső függősége. Cserébe a C-s nincs 6 kB se (strippelve), míg a Pascalos 30 kB felett van. Mielőtt a főprogramra térnénk, először meg kell csinálni a library-hoz tartozó API-t. C esetén ez egy header file (mylib.h) képében fog manifesztálódni (lehetne .c is .h helyett; de így nem fog ütközni a neve a library-éval), Pascal esetén pedig egy unit file (mylib.pp, szintén az ütközést preventálandó nem .pas). Az API fog felelni azért, hogy betöltse/felszabadítsa a library-t, illetve, hogy a megfelelő szimbólumokat a főprogram számára használható függvény-prototípusokhoz kösse. Kezdjük a betöltéssel és a bezárással. Ezt C alatt a dlopen() függvénnyel lehet elérni (manualok: POSIX, Linux, FreeBSD, OpenBSD, Solaris), Pascal alatt a LoadLibrary() függvénnyel. Utóbbinak csak a library útvonala kell (elég a fájlnév is, ha a library olyan könyvtárban van, ami szerepel a LD_LIBRARY_PATH környezeti változóban), előbbinek azonban van egy második paramétere is, ami azt határozza meg, hogy milyen megközelítéssel töltse be, vagy épp szabadítsa fel a library-t, végezze el a relokálásokat (azon szimbólumok referenciáinak helyes címre való átirányítását, amiknek a címe csak a betöltődés után lesz ismert) és szabályozza a szimbólumok hozzáférhetőségét is. Az RTLD_NOW flag használata (ill. Linux alatt a LD_BIND_NOW környezeti változó bármilyen - nem üres - stringre való beállítása) azt eredményezi, hogy az összes relokációt azonnal elvégzi a program, míg a RTLD_LAZY flag használata esetén csak akkor történik meg egy adott szimbólum relokálása, ha hozzá akar férni a program. Az RTLD_GLOBAL flag azért felel, hogy a betöltött library szimbólumai hozzáférhetőek legyenek más a program által betöltött library-k számára is, a RTLD_LOCAL pedig ennek ellentétét jelenti. Namármost, mivel globális hozzáférhetőség esetén az így betöltött library-k szimbólumai azonos nevek esetén ütközni fognak és a pluginek mind szükségszerűen tartalmazni fogják azokat a szimbólumokat, amikkel a program kapcsolódni tud hozzájuk, így ebből kiderül, hogy pluginek esetén a RTLD_LOCAL használata javallott. A feloldási megközelítés alapvetően mindegy, de a program működésétől függően lehet a RTLD_LAZY flag-gel teljesítménynövekedést elérni, ha pl. a program adott körülmények között adott szimbólumokhoz egyáltalán nem nyúl. A többi - egyébként non-POSIX - flag minket most nem érint. Sikeres betöltés esetén egy pointert kapunk, ami a library handle-jére mutat, sikertelen esetben ez NULL, ill. Pascalban DynLibs.NilHandle (ami a gyakorlatban ugyanúgy nullát jelent, csak Int64 típussal). Azt viszont nem árt tudni, hogy bár újbóli kinyitási kísérlet esetén újból ugyanazt a handle-t fogjuk visszakapni, mert nem tölti be többször a library-t - viszont pont azért, mert a kódban akár több egymástól független rész is betölthet egy adott library-t - felszabadítani a library-t csak ugyanannyi bezárással lehet, mint ahányszor megnyitottuk. Bezárni pedig a dlclose() függvénnyel lehet a library-t C alatt (manualok: POSIX, Solaris, a többieké ugyanott van, ahol a dlopen()-é is) és a FreeLibrary() függvénnyel Pascal alatt. Ennek megfelelően a betöltés és bezárás C-ben: #include <dlfcn.h> ... void *handle = dlopen("libmylib.so", RTLD_NOW | RTLD_LOCAL); dlclose(handle);És Pascalban: uses DynLibs; var handle: TLibHandle; ... handle := LoadLibrary('libmylib.so'); FreeLibrary(handle);Folytassuk a relokálással. A szimbólumok konkrét feloldásáért/relokálásáért a dlsym() függvény felel C alatt (manualok: POSIX, Solaris, a többieké pedig ismét ugyanott található, ahol a dlopen()) és a GetProcedureAddress() függvény Pascal alatt. Példa C-ben: int (*mylib_a)(int a); ... mylib_a = dlsym(handle, "mylib_a");És Pascalban: type Tmylib_a = function (a: longint): longint; cdecl; var mylib_a: Tmylib_a; ... mylib_a := Tmylib_a(GetProcedureAddress(handle, 'mylib_a'));Most már tkp. be tudjuk tölteni a library-t és használni a függvényeit. Mivel azonban itt pluginekről beszélünk, azaz több library-nk lesz, azonos függvényekkel, így mindenképpen szükség lesz valamilyen szintű "konténeresítésre", hogy egyrészt ne akadjanak össze (ezúttal nem szimbólumszinten, mint feljebb, hanem API szinten), másrészt meg, hogy tudjuk, hogy melyik relokált függvény melyik handle-höz is tartozik. Ezt lehet akár handle és funkciótömbökkel is csinálni, de sokkal egyszerűbb, ha bepakoljuk egy struct-ba typedef struct { void *handle; int (*mylib_a)(int a); void (*mylib_b)(int *b); int (*mylib_c)(int *c); } mylib;vagy record-ba type Tmylib_a = function (a: longint): longint; cdecl; Tmylib_b = procedure (b: plongint); cdecl; Tmylib_c = function (c: plongint): longint; cdecl; Tmylib = record handle: TLibHandle; mylib_a: Tmylib_a; mylib_b: Tmylib_b; mylib_c: Tmylib_c; end; Pmylib = ^Tmylib;és akkor minden pluginhez tartozhat egy ilyen konténer. Valamivel fentebb tisztáztuk, hogy egy ilyen plugint egyszer nyitunk ki, így ezt nem ártana pl. lekezelni a lezáráskor (C) void close_mylib(mylib *lib) { if ((lib != NULL) && (lib->handle != NULL)) { dlclose(lib->handle); lib->handle = NULL; } }(Pascal) procedure close_mylib(lib: Pmylib); begin if ((lib <> nil) and (lib^.handle <> DynLibs.NilHandle)) then begin FreeLibrary(lib^.handle); lib^.handle := DynLibs.NilHandle; end; end;és a megnyitáskor is. C: #define MYLIB_ERROR_INVALID_CONTAINER 1 #define MYLIB_ERROR_ALREADY_OPEN 2 #define MYLIB_ERROR_CANNOT_OPEN 3 #define MYLIB_ERROR_MISSING_SYMBOL 4 int open_mylib(mylib *lib, char *path) { if (lib == NULL) { return MYLIB_ERROR_INVALID_CONTAINER; } if (lib->handle != NULL) { return MYLIB_ERROR_ALREADY_OPEN; } lib->handle = dlopen(path, RTLD_NOW | RTLD_LOCAL); if (lib->handle == NULL) { return MYLIB_ERROR_CANNOT_OPEN; } lib->mylib_a = dlsym(lib->handle, "mylib_a"); if (lib->mylib_a == NULL) { close_mylib(lib); return MYLIB_ERROR_MISSING_SYMBOL; } lib->mylib_b = dlsym(lib->handle, "mylib_b"); if (lib->mylib_b == NULL) { close_mylib(lib); return MYLIB_ERROR_MISSING_SYMBOL; } lib->mylib_c = dlsym(lib->handle, "mylib_c"); if (lib->mylib_c == NULL) { close_mylib(lib); return MYLIB_ERROR_MISSING_SYMBOL; } return 0; }Pascal: const MYLIB_ERROR_INVALID_CONTAINER = 1; MYLIB_ERROR_ALREADY_OPEN = 2; MYLIB_ERROR_CANNOT_OPEN = 3; MYLIB_ERROR_MISSING_SYMBOL = 4; function init_mylib(lib: Pmylib; path: string): longint; begin if (lib = nil) then begin result := MYLIB_ERROR_INVALID_CONTAINER; exit; end; if (lib^.handle <> DynLibs.NilHandle) then begin result := MYLIB_ERROR_ALREADY_OPEN; exit; end; lib^.handle := LoadLibrary(path); if (lib^.handle = DynLibs.NilHandle) then begin result := MYLIB_ERROR_CANNOT_OPEN; exit; end; lib^.mylib_a := Tmylib_a(GetProcedureAddress(lib^.handle, 'mylib_a')); if (lib^.mylib_a = Tmylib_a(DynLibs.NilHandle)) then begin close_mylib(lib); result := MYLIB_ERROR_MISSING_SYMBOL; exit; end; lib^.mylib_b := Tmylib_b(GetProcedureAddress(lib^.handle, 'mylib_b')); if (lib^.mylib_b = Tmylib_b(DynLibs.NilHandle)) then begin close_mylib(lib); result := MYLIB_ERROR_MISSING_SYMBOL; exit; end; lib^.mylib_c := Tmylib_c(GetProcedureAddress(lib^.handle, 'mylib_c')); if (lib^.mylib_c = Tmylib_c(DynLibs.NilHandle)) then begin close_mylib(lib); result := MYLIB_ERROR_MISSING_SYMBOL; exit; end; result := 0; end;(Pascalban természetesen ezt a két függvényt exportálni kell, C-ben pedig be kell húzni a stdlib.h-t a NULL-hoz.) Ezzel le is tudtuk az API-t, most már jöhet a főprogram. Ebben semmi mást nem kell csinálni, mint behúzni az API-t és meghívogatni a megfelelő függvényeket a megfelelő paraméterrel, pl. a fájlnévvel és a deklarált konténerstruktúrával. Ezt most az egyszerűség kedvéért egy mezei globális változóban tároljuk; nyilván ezt allokálni kell mindegyik pluginhez, ha több pluginünk lesz, de szemléltetésnek most így is megteszi. C-ben: char *libname = "./libmylib.so"; int err, a, b, c, c2; mylib lib; ... lib.handle = NULL; rr = open_mylib(&lib, libname); if (err != 0) { fprintf(stderr, "Cannot open \"%s\" - error %d.\n", libname, err); return 1; } a = lib.mylib_a(9); b = 10; lib.mylib_b(&b); c = 11; c2 = lib.mylib_c(&c); fprintf(stdout, "a = %d, b = %d, c = %d, c2 = %d\n", a, b, c, c2); close_mylib(&lib);És Pascalban: const libname = './libmylib.so'; var err, a, b, c, c2: longint; lib: Tmylib; ... lib.handle := DynLibs.NilHandle; err := init_mylib(@lib, libname); if (err <> 0) then begin WriteLn(StdErr, 'Cannot open "' + libname + ' - error ' + IntToStr(err) + '.'); Halt(1); end; a := lib.mylib_a(9); b := 10; lib.mylib_b(@b); c := 11; c2 := lib.mylib_c(@c); WriteLn(StdOut, 'a = ' + IntToStr(a) + ', b = ' + IntToStr(b) + ', c = ' + IntToStr(c) + ', c2 = ' + IntToStr(c2)); close_mylib(@lib);Pascalban ne felejtsük el a DynLibs unitot is behúzni a DynLibs.NilHandle használatához. (És persze a SysUtils a WriteLn()-hez és C-ben a stdio.h a fprintf()-hez.) Ha megvan a főprogram, lehet fordítani. C-ben a dl lib kelleni fog a dinamikus library-khoz. cc mytest.c -ldl -o mytestPascalban itt is elég csak simán megküldeni a fordítóval. fpc mytest.pas(Pascalban egyébként itt is és a librarynál is érdemes a -CX -Xs -XX kapcsolókat használni a smartlinkinghez és a strippinghez.) És ezzel tulajdonképpen készen is vagyunk, a library működik. Illetve egy dolog még hátra van: az az eset, amikor nem a főprogram hívja meg a library valamelyik függvényét, hanem fordítva. Ezt kétféleképpen lehet megoldani: lokálisan és globálisan. Abban az esetben, amikor valamilyen aszinkron visszajelzést akarunk csinálni - azaz az az egy darab függvény fogja meghívni a főprogram valamely függvényét, akkor, amikor a főprogram őt meghívja - és a feladattól függően akár változhat, hogy mit is akarunk meghívatni, abban az esetben ezeket a callback függvényeket célszerű közvetlenül átadni a library egyes függvényeinek, tehát lokálisan megoldani. Amik viszont különféle segédfunkciókat valósítanak meg és több függvény is használhatja őket, bármikor, azokat az összes függvénynek el kell tudnia érni, azaz globálisan kell megoldani. Ez utóbbit két megközelítéssel lehet csinálni: automatikusan és manuálisan. Automatikus megközelítés esetén a library-ben külsőnek megjelölt prototípusokat helyezünk el, a főprogramot pedig dinamikusan linkeljük; ilyenkor megnyitáskor a library fogja a saját függvényéhez kötni a főprogram szimbólumait. Legyen egy új függvényünk a library kódjában. C: int mylib_d(int *d) { *d += 5; return mylib_caller(d); }Pascal: function mylib_d(d: plongint): longint; cdecl; begin d^ := d^ + 5; result := mylib_caller(d); end;Ne felejtsük el az interface fájlban az új függvényt betenni a "konténerbe", feloldani az inicializálófüggvényben, valamint Pascalban ne felejtsük el felvenni a library-ban az exports listára sem. Ez egy egyelőre még nem létező függvényt hív meg, ennek kell még a prototípusa. C: extern int mylib_caller(int *ptr);Pascal: function mylib_caller(ptr: plongint): longint; cdecl; external name 'mylib_caller';Ezek után már csak a főprogramba kell bebiggyeszteni ezt az új függvényt. C: int mylib_caller(int *ptr) { *ptr += 5; return *ptr + 5; }Pascal: function mylib_caller(ptr: plongint): longint; cdecl; begin ptr^ := ptr^ + 5; result := ptr^ + 5; end;És persze a meghívását is. C: d = 12; d2 = lib.mylib_d(&d); fprintf(stdout, "a = %d, b = %d, c = %d, c2 = %d, d = %d, d2 = %d\n", a, b, c, c2, d, d2);Pascal: d := 12; d2 := lib.mylib_d(@d); WriteLn(StdOut, 'a = ' + IntToStr(a) + ', b = ' + IntToStr(b) + ', c = ' + IntToStr(c) + ', c2 = ' + IntToStr(c2) + ', d = ' + IntToStr(d) + ', d2 = ' + IntToStr(d2));Ne felejtsük el a két új változót felvenni és Pascalban exportálni a főprogramban a függvényt. (exports mylib_caller;) A library fordítási opciói nem változnak, a főprogramé viszont igen: a linkernek át kell adni az --export-dynamic kapcsolót. Ezt C-ben a már fentebb használt -Wl segítségével lehet, míg Pascalban a -k felel ugyanezért. Tehát C: cc mytest.c -ldl -Wl,--export-dynamic -o mytestPascal: fpc -k--export-dynamic mytest.pas(A Pascalnál egy kellemetlen mellékhatása ennek a megközelítésnek, hogy a kapott bináris tízszer akkora lesz, mert egy tonna egyéb szimbólumot is belefordít...) A manuális megközelítés esetén a külső függvényeknek pointereket kell betenni a library-be, a főprogramot pedig nem kell dinamikusan linkelni (azaz itt nem változik a főprogram fordítása az alap felálláshoz képest), hanem helyette a főprogramnak kell feloldania eme pointerek szimbólumait és betöltenie a helyükre az odavágó rutinjainak címét. Tehát C-ben a függvény ezúttal így néz ki: int (*mylib_caller)(int *ptr);Pascalban pedig így: var mylib_caller: function (ptr: plongint): longint; cdecl; CVar;Ellentétben az automatikus megközelítéssel, itt értelemszerűen ezt a szimbólumot be kell tenni az interface-ben a konténerbe és fel is kell oldani, hogy a főprogram hozzáférjen. Viszont az interface-ben itt nem maga a függvény pointere kell, hanem egy arra mutató pointer, hogy a főprogram be tudja írni a megfelelő értéket. C-ben typedef struct { ... int (**mylib_caller)(int *ptr); }Pascalban pedig type Tmylib_caller = function (ptr: plongint): longint; cdecl; Pmylib_caller = ^Tmylib_caller; Tmylib = record ... mylib_caller: Pmylib_caller; end; Pmylib = ^Tmylib;lesz a felállás és Pascal esetén a feloldás is ennek megfelelően alakul: Pmylib_caller-re kell castolni, nem közvetlenül a Tmylib_caller-re. A főprogram majdnem 100%-ban ugyanaz, mint az automatikus esetén, a különbség csak az, hogy itt a library megnyitása és a használata közé még be kell ékelni egy *(lib.mylib_caller) = &mylib_caller;sort C-ben és egy (lib.mylib_caller)^ := @mylib_caller;sort Pascalban, illetve ez utóbbiban az is eltér, hogy az exports mylib_caller; itt nem a főprogramban kell, hanem a library-ben. Hátra van még a callback-es felállás, az (értelemszerűen) ugyanaz mind automatikus, mind manuális linkelésnél: van egy függvényünk a library-ben, aminek át lehet adni egy másik függvény pointerét, amit belül meghívhat. C: void mylib_e(int e, void (*callback_ptr)(int)) { callback_ptr(e + 5); }Pascal: type Tmylib_callback = procedure (val: longint); cdecl; procedure mylib_e(e: longint; callback_ptr: Tmylib_callback); cdecl; begin callback_ptr(e + 5); end;Exportálást Pascalban nem elfelejteni, ahogy az interface-ben a feloldást és a konténerbe való felvételt sem (C) typedef struct { ... void (*mylib_e)(int e, void (*callback_ptr)(int)); }(Pascal) type Tmylib_callback = procedure (val: longint); cdecl; Tmylib_e = procedure (e: longint; callback_ptr: Tmylib_callback); cdecl; Tmylib = record ... mylib_e: Tmylib_e; end; Pmylib = ^Tmylib;Marad a főprogram, ahol először is kell maga a callback függvény (C) void mylib_callback1(int val) { e = val + 5; }(Pascal) procedure mylib_callback1(val: longint); cdecl; begin e := val + 5; end;és aztán a tényleges használat (C) e = 13; lib.mylib_e(e, &mylib_callback1); fprintf(stdout, "a = %d, b = %d, c = %d, c2 = %d, d = %d, d2 = %d, e = %d\n", a, b, c, c2, d, d2, e);(Pascal) e := 13; lib.mylib_e(e, @mylib_callback1); WriteLn(StdOut, 'a = ' + IntToStr(a) + ', b = ' + IntToStr(b) + ', c = ' + IntToStr(c) + ', c2 = ' + IntToStr(c2) + ', d = ' + IntToStr(d) + ', d2 = ' + IntToStr(d2) + ', e = ' + IntToStr(e));(Az új e változó itt értelemszerűen globális, hiszen a fő függvényen kívül a callback függvény is közvetlenül fér hozzá.) Nos, nagyjából ennyi lenne a pluginezés, legalábbis, ami a lényeget illeti, bár biztos van egy-két specifikus dolog, ami nem lett kivesézve. Pl. arra nem tértem ki, hogy mi van, ha a pluginek egymás függvényeit akarják hívogatni; nos ezt értelemszerűen úgy lehet megoldani, hogy vagy van a főprogramban egy wrapper/transmitter-callback erre a célra, vagy nemes egyszerűséggel a főprogram, miután feloldotta a szimbólumokat mind a két (vagy több) pluginben, egyszerűen az egyes pluginok megfelelő callback pointereire beírja a másik plugin függvényeinek címét. (Ez utóbbi felállás egyébként nem célszerű; két mezei library (nem plugin) közt simán működhet, még két eltérő interface-ű plugin közt is, bár ott is csak 1-1 közt, viszont azonos interface-ű pluginek garmadája esetén symbol-spagetti szintű, sok sikert kategóriás a dolog.) Ha esetleg valami nem lenne tiszta, a komplett példákat, buildscripttel, tokkal-vonóval le lehet tölteni ezekről a linkekről: C - Basic setup Pascal - Basic setup C - Automatically linked setup Pascal - Automatically linked setup C - Manually linked setup Pascal - Manually linked setup Egyébként írtam egy-egy alap unitot (C-hez és Pascalhoz is), ami ezeknek a feladatoknak a többségét elvégzi; allokálja a struct-hoz/record-hoz szükséges tárterületet, majd feloldja az átadott szimbólumokat és ha van callback pointer beállítva, akkor a szimbólum címére beírja azt is. (Direct cross-plugin calling not supported. :P) Kicsit közérthetőbben pl. a fenti manuálisan linkelt library használata így nézne ki C-ben vele: #include <stdio.h> #include "dynlib_handler.c" typedef struct { void *handle; int (*mylib_a)(int a); void (*mylib_b)(int *b); int (*mylib_c)(int *c); int (*mylib_d)(int *d); void (*mylib_e)(int e, void (*callback_ptr)(int)); void (**mylib_callback)(int *ptr); } dynlib_handle; int mylib_callback(int *ptr) { *ptr += 5; return *ptr + 5; } dynlib_symbol_entry lib_entries[] = { { "mylib_a", (void *)NULL }, { "mylib_b", (void *)NULL }, { "mylib_c", (void *)NULL }, { "mylib_d", (void *)NULL }, { "mylib_e", (void *)NULL }, { "mylib_caller", (void *)&mylib_callback } }; int lib_entry_count = sizeof(lib_entries) / sizeof(dynlib_symbol_entry); int e; void mylib_callback1(int val) { e = val + 5; } int main() { int err, serr, a, b, c, c2, d, d2; char *libname = "./libmylib.so"; dynlib_handle *lib; lib = create_dynlib_handler(libname, lib_entries, lib_entry_count, &err, &serr); if (err != 0) { switch (err) { case DYNLIB_HANDLER_ERROR_CANNOT_ALLOC_MEMORY: fprintf(stderr, "Cannot allocate memory.\n"); break; case DYNLIB_HANDLER_ERROR_CANNOT_OPEN_LIBRARY: fprintf(stderr, "Cannot open \"%s\".\n", libname); break; case DYNLIB_HANDLER_ERROR_MISSING_SYMBOL: fprintf(stderr, "Missing symbol \"%s\".\n", lib_entries[serr].symbol); break; } return e; } a = lib->mylib_a(9); b = 10; lib->mylib_b(&b); c = 11; c2 = lib->mylib_c(&c); d = 12; d2 = lib->mylib_d(&d); e = 13; lib->mylib_e(e, &mylib_callback1); fprintf(stdout, "a = %d, b = %d, c = %d, c2 = %d, d = %d, d2 = %d, e = %d\n", a, b, c, c2, d, d2, e); free_dynlib_handler(lib); return 0; }És így Pascalban: program mytest; {$mode objfpc}{$H+} uses dynlib_handler, SysUtils; type Tmylib_callback = procedure (val: longint); cdecl; Tmylib_a = function (a: longint): longint; cdecl; Tmylib_b = procedure (b: plongint); cdecl; Tmylib_c = function (c: plongint): longint; cdecl; Tmylib_d = function (d: plongint): longint; cdecl; Tmylib_e = procedure (e: longint; callback_ptr: Tmylib_callback); cdecl; Tmylib_caller = function (ptr: plongint): longint; cdecl; Pmylib_caller = ^Tmylib_caller; Tdynlib_handle = record lib: TLibHandle; mylib_a: Tmylib_a; mylib_b: Tmylib_b; mylib_c: Tmylib_c; mylib_d: Tmylib_d; mylib_e: Tmylib_e; mylib_caller: Pmylib_caller; end; Pdynlib_handle = ^Tdynlib_handle; var err, serr, a, b, c, c2, d, d2, e: integer; lib: Pdynlib_handle; function caller(ptr: pinteger): integer; cdecl; begin ptr^ := ptr^ + 5; result := ptr^ + 5; end; procedure mylib_callback1(val: longint); cdecl; begin e := val + 5; end; const lib_entries: Tdynlib_symbols = ( ( symbol: 'mylib_a'; ext_func: nil ), ( symbol: 'mylib_b'; ext_func: nil ), ( symbol: 'mylib_c'; ext_func: nil ), ( symbol: 'mylib_d'; ext_func: nil ), ( symbol: 'mylib_e'; ext_func: nil ), ( symbol: 'mylib_caller'; ext_func: @caller ) ); libname = './libmylib.so'; begin lib := Pdynlib_handle(create_dynlib_handler(libname, @lib_entries, @err, @serr)); if (err <> 0) then begin case err of DYNLIB_HANDLER_ERROR_CANNOT_ALLOC_MEMORY: begin WriteLn(StdErr, 'Cannot allocate memory.'); end; DYNLIB_HANDLER_ERROR_CANNOT_OPEN_LIBRARY: begin WriteLn(StdErr, 'Cannot open ' + libname + '.'); end; DYNLIB_HANDLER_ERROR_MISSING_SYMBOL: begin WriteLn(StdErr, 'Missing symbol ' + lib_entries[serr].symbol + '.'); end; end; Halt(e); end; a := lib^.mylib_a(9); b := 10; lib^.mylib_b(@b); c := 11; c2 := lib^.mylib_c(@c); d := 12; d2 := lib^.mylib_d(@d); e := 13; lib^.mylib_e(e, @mylib_callback1); WriteLn(StdOut, 'a = ' + IntToStr(a) + ', b = ' + IntToStr(b) + ', c = ' + IntToStr(c) + ', c2 = ' + IntToStr(c2) + ', d = ' + IntToStr(d) + ', d2 = ' + IntToStr(d2) + ', e = ' + IntToStr(e)); free_dynlib_handler(PPointer(lib)); end.A unit is letölthető ezekről a linkekről: C version Pascal version Asszem ennyi. Remélem sajtóhiba nem lesz a cikkben... :P Bikacsököt bilgécnek és a kurwa anyját a mikrofosnak! |