Newsgroup: it.comp.programmare.win32
Titolo: LoadLibrary

Una volta caricate una libreria in memoria con il codice
HANDLE hLib;
hLib = LoadLibrary (TEXT("Dlltest.dll"));
come posso utilizzarne le funzioni, ad esempio int moltiplica (int x, int
y)?
Ho provato con la funzione GetProcAddress, ma non riesco ad utilizzarla.
Grazie 1000

, 
mn...@libero.it
 
says...

Attento al name mangling (o come si scrive...) del C++.
Le stringhe che indicano un nome di funzione/metodo del C++ sono 
modificate rispetto al nome che tu specifichi, per tenere conto dei 
parametri della funzione (il compilatore C++ deve poter distinguere le 
forme overloadate di una funzione: "void prova(int)" e "void prova
(TestObject *)").
Il mio consiglio e' di usare la convenzione C per i nomi delle funzioni 
che esporti da una DLL. extern "C" <funzione>
ciao

Attento al name mangling (o come si scrive...) del C++.
Per evitare questo problema ,chiama la funzione attraverso il suo
ordinal value .

, 
nik...@inwind.it
 
says...

IMO e' meglio usare i nomi di funzione con le convenzioni di naming del 
C (= senza mangling), piuttosto che usare gli ordinal value (mi pare che 
qualcosa del genere fosse anche scritta sul testo di Richter Advanced 
Windows Programming...)


Vero, se si inserisce una funzione esportata in mezzo alle altre
ti cambia l'ordinal value di quelle successive e se non te ne accorgi
poi son dolori.
La cosa migliore da fare e' esportare le funzioni senza name mangling
usando la standard calling convention (quella delle funzioni di sistema
Win32).
Se quel che gestisce la funzione e' strutturato ad oggetti conviene
usare handle al posto dei puntatori ad oggetto ed al costo
di un lieve appesantimento della DLL la si puo usare praticamente
con qualsiasi linguaggio (VB, Delphi, Python, ecc.) e compilatore C++
(il name mangling fatto da Visual C++, C++ Builder e GCC e' differente).
Giusto per fornire un riferimento, un buon modello da seguire
e' quello delle librerie OpenGL.
L. Micheletto

Va bene per il fatto del name mangling, ma potresta farmi un esempio di come
si usa la funzione GetProcAddress?
Grazie

 di come si usa la funzione GetProcAddress?
Cerca GetProcAddress su MSDN.
C'č veramente poco da capire.
Kiuhnm

 si usa la funzione GetProcAddress?
Si tratta di una funzione API molto semplice.
Vuole in input un handle alla DLL da cui vuoi importare la funzione 
(handle tipicamente ritornato dalla LoadLibrary), ed il nome della 
funzione che vuoi importare.
Come valore di ritorno ti da' un puntatore alla funzione, che puoi 
quindi richiamare.
Per esempio, supponi che nella dll "topsecret.dll" ci sia una funzione 
chiamata "easter_egg", che abbia un prototipo del tipo:
void easter_egg(HWND parent_window);
Allora, per lanciare questo ipotetico easter-egg, ti basta:
// ** Scheletro di codice... ** //
//
// Carica la DLL
//
HINSTANCE dll = LoadLibrary("topsecret.dll");
assert(dll != NULL);
//
// Carica la funzione "easter_egg"
// contenuta nella DLL
//
// Alias di comodo per type-cast
typedef void (*easter_egg_type)(HWND);
// Puntatore alla funzione da importare
easter_egg_type f;
// Carica la funzione dalla DLL
f = (easter_egg_type) GetProcAddress(dll, "easter_egg");
assert(f != NULL);
// Chiama la funzione
f(window);
// Chiudi la DLL
FreeLibrary(dll);
// Giusto per sicurezza ;-) affinche' non usi i puntatori
// nel codice successivo...
f = NULL;
dll = NULL;
// ** Fine scheletro di codice ** //
Ovviamente le assert(...) vanno sostituite con qualcosa di piu' 
"robusto" (tipo stampare messaggi di errore, etc.)

 funzioni di sistema Win32).
no, no, __stdcall si limita a cambiare *un* carattere di tutto il nome 
decorato. I nomi da esportare vanno dichiarati come funzioni C, con il 
costrutto extern:
extern "C"
{
 int moltiplica (int x, int y);
}
se s'hanno da esportare oggetti, li si può o portare in giro come 
semplici puntatori, oppure usare COM (*non* OLE)
-- 
 < 
http://www.reactos.com/
 >  Open source clone of Windows NT. Current
     Don't stand, REACT!      version 0.0.20. C, C++ and ASM developers
                              and beta testers are welcome!

Su VC++ (non so se e' il tuo caso) io faccio cosi'
Supponiamo che la tua dll deve esportare due funzioni: LogIn e LogOut.
Dichiarale cosi':
#define MYAPI  __declspec(dllexport) __stdcall
extern "C" void MYAPI LogIn(void* dataLogIn)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
      ... codice ......
}
extern "C" void MYAPI LogOut(void* dataLogOut)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
 
        ... codice .........
}
La macro AFX_MANAGE_STATE(AfxGetStaticModuleState()) e'
necessaria per eviare casini....
Ora apri il file .def presente nel progetto della dll.
Aggiungi queste righe
LogIn				@74
LogOut				@75
( i numeri sono a caso...)
Compila il tutto e procedi come indicato dalla mail di CodeNinja.
saluti.
DB

 semplici puntatori, oppure usare COM (*non* OLE)
Se li porti in giro come puntatori ci rimetti in termini di
robustezza del codice, se usi COM ci rimetti in portabilita.
Io uso handle ricavati da indici (su tabella di risorse)
con checksum/CRC semplificati (dipende dal livello
di robustezza che voglio ottenere), in questo modo
la DLL puo "riconoscere" bug nel codice dell'applicazione
chiamante (diagnostica bottom-up) ed inoltre (grazie
ad un secondo livello di validazione nelle strutture
referenziate) rende impossibile al codice chiamante
un errata gestione delle strutture dati o degli oggetti gestiti
dalla DLL.
Come ho scritto nel messaggio precedente, mi interessa
la robustezza E la portabilita quando scrivo una DLL.
L. Micheletto

 la robustezza E la portabilita quando scrivo una DLL.
Sono d'accordo.
Ciao,
CodeN

 dalla DLL.
Oops! Mi correggo, "impossibile" no, ma elimina parecchie
rogne e se ci sono problemi e' molto piu facile
capire se la colpa e' della DLL oppure dell'applicazione
che la richiama.
Piu che altro rende molto difficile scaricare sulla DLL
errori dell'applicazione, cosa *molto* importante
per rispedire velocemente al mittente certe chiamate fatte
allo scadere dell'orario d'ufficio. <eg>
un Lorenzo e le buone tecniche di codifica che migliorano
            la qualita della vita :-)

 O anche solo segnalarmi dei link a riguardo? Grazie molte!
Il "trucco" sta tutto nell'usare l'handle come una specie di
chiave difficile da produrre per caso.
Di solito se devo scrivere una DLL, lo faccio solo per gestire
oggetti "grossi" (es: un modulo che gestisce un protocollo
di comunicazione sulla porta seriale) in modo tale che
l'overhead della chiamata della funzione e di eventuali
conversioni dei parametri sia relativamente trascurabile.
Internamente alla DLL ho delle strutture o degli oggetti
che rappresentano quel che sto manipolando (es: nel caso
di un gestore di protocollo, vari dati riguardanti
la porta seriale e lo stato dell'automa a stati finiti
che gestisce invio/ricezione secondo il protocollo
utilizzato), ma all'esterno della DLL non voglio
passare puntatori a strutture interne.
A questo punto implemento un meccanismo di conversione
per cui i vari "oggetti" sono contenuti in una tabella
(allocata & ridimensionata dinamicamente in base alle
  esigenze) oppure in una lista, in modo tale che li si possa
referenziare in base al loro numero d'ordine.
Per ridurre la possibilita di errori applico una funzione
hash reversibile che dato un indice produce una "firma"
da usare come handle con eventualmente dei bit di checksum
o di crc in modo tale che se la DLL viene chiamata con handle
sballati sia possibile almeno in una percentuale significativa
di casi capire che l'handle e' errato.
Un metodo "stupido" ad esempio consiste in una funzione hash
che calcola (((indice*costante) + offset) xor maschera) dove
costante, offset e maschera devono essere tali che 0 non sia
mai un handle valido, in questo modo per passare da handle
ad indice basta eseguire (((handle xor maschera) - offset)/costante).
Quando poi ottengo l'indice, verifico che sia compreso
tra il minimo ed il massimo degli indici di risorse allocate
e che la risorsa stessa non sia stata deallocata da chiamate
precedenti (n.b. uso una tabella/lista per cui dopo aver
allocato una risorsa, se non e' l'ultima non la rilascio
quando non mi serve piu, ma la marco come "riutilizzabile").
Questo gia fornisce una certa protezione, se poi l'handle
viene spezzato in due parti dove la seconda deve coincidere
con una seconda firma memorizzata nell' "oggetto"
associato all'handle si ha un livello aggiuntivo di protezione.
Come ho scritto in precedenza la complessita del meccanismo
di verifica varia in base a quel che si deve realizzare, ma
gia il fatto di rendere piu difficile la generazione casuale
di un handle valido fornisce una buona protezione nei confronti
di bug nell'applicazione chiamante.
Per esempio, se prevedo un limite massimo di 64K sessioni
aperte simultaneamente (e' un limite fin troppo ragionevole
nel caso di un gestore di porta seriale) ed uso handle
a 32bit, se per errore uso handle "casuali" ho una
probabilita su 65536 che l'handle "casuale" venga accettato
dalla DLL (e questo nel caso peggiore con 64K connessioni
attive).
E' una tecnica "stupida", ma se devi passare delle DLL
a dei clienti esterni, ti risparmia un sacco di grattacapi
visto che possono usarle anche con linguaggi che non
supportano i puntatori (tipo VB) e poi diventa piu semplice
capire chi-sbaglia-cosa.
L. Micheletto

 robustezza del codice, se usi COM ci rimetti in portabilita.
veh, che g++ ha -fvtable-thunks da una vita e mezza. E COM, con un po' 
di masochismo, si usa anche in C. Ripeto che sto parlando di COM, *non* 
di OLE. COM è una fesseria, si tratta di un'astrazione portabile di 
reference counting e (quello che ci interessa) typecast controllato. 
Probabilmente tu stai pensando ad OLE


 > O anche solo segnalarmi dei link a riguardo? Grazie molte!
[CUT]
Io nelle DLL che ho scritto - per hobby - esportavo sempre i 
puntatori... il sistema da te suggerito e' di gran lunga piu' 
"professionale"!
Mi hai fornito degli spunti interessantissimi; ti ringrazio molto!
Ciao,
CodeN

Maya wrote:
 > Lorenzo Micheletto ha scritto:
 >
 >
 >>>se s'hanno da esportare oggetti, li si può o portare in giro come
 >>>semplici puntatori, oppure usare COM (*non* OLE)
 >>
 >>Se li porti in giro come puntatori ci rimetti in termini di
 >>robustezza del codice, se usi COM ci rimetti in portabilita.
 >
 >
 > veh, che g++ ha -fvtable-thunks da una vita e mezza. E COM, con un po'
 > di masochismo, si usa anche in C. Ripeto che sto parlando di COM,*non*
 > di OLE. COM è una fesseria, si tratta di un'astrazione portabile di
 > reference counting e (quello che ci interessa) typecast controllato.
 > Probabilmente tu stai pensando ad OLE
Li ho presenti entrambi, ma COM va bene se si codifica qualcosa
che nasce e muore sotto Windows, senza contare che di solito
le DLL le scrivo in C, in questo modo e' piu semplice portarle
su target embedded (linkate staticamente e rimuovendo gli handle
in modo da usare direttamente puntatori).
Mai visti Visual C++ e COM per h8/300H, ma GCC si (usato come
semplice compilatore C, il lato C++ e' sempre carente oppure
in ritardo nelle cpu embedded).
L. Micheletto

 che nasce e muore sotto Windows,
ripeto se non fosse chiaro: g++ ha -fvtable-thunks, da un bel po' di 
versioni. Non suona un campanello? e g++ ha notoriamente problemi di 
compatibilità binaria da versione a versione. Ti dice ancora niente?
COM è solo un'ABI (v-table) combinata con un'API (IUnknown), entrambe 
banali, che servono ad esporre il meno possibile dell'implementazione 
di compilatore e sistema, astraendo due tra le più problematiche parti 
non standardizzate del C++: il name mangling e la RTTI


 non standardizzate del C++: il name mangling e la RTTI
Ripeto pure io, il mondo non ruota attorno al C++ o a COM.
Un modello basato su handle ed implementato correttamente
permette di ottenere moduli che espongono oggetti di vario
tipo *molto* semplici da utilizzare, *molto* semplici da adattare
alle proprie esigenze e con una buona indipendenza dal linguaggio
e dal sistema operativo utilizzato.
Per esempio:
Anni fa mi sono trovato a dover scegliere una API 3D da utilizzare
per una applicazione sotto Windows e scritta in Visual Basic, le
alternative erano OpenGL e DirectX 3 (di cui avrei usato Direct3D), in
quel periodo non c'erano wrapper gia pronti per usare Direct3D
da VB, bisognava farseli da soli, OpenGL (basato su handle) invece
richiedeva solo dei declare per essere utilizzato ed esponeva al
programmatore un modello piu semplice da utilizzare in modo
incrementale. E stiamo parlando di Visual Basic!!!
Proprio quel VB che in teoria dovrebbe andare a braccetto
con COM ed OLE/ActiveX.
Con Windows il minimo comun denominatore non e' COM, sono le
funzioni con convenzione di chiamata  _stdcall (alias APIENTRY)
e gli handle, se si vuole realizzare una DLL "per tutti"
e' quella la via da seguire, se invece si ha un target
piu ristretto si puo usare tutto il resto.
L. Micheletto