Newsgroup: it.comp.lang.visual-basic
Titolo: Creare dll in Visual Basic

Ciao a tutti.
Il mio problema è questo: vorrei poter mettere le mie funzioni
all'interno di dll personalizzate da poter utilizzare nei miei programmi
in modo che, una volta registrate, posso utilizzarle dichiarandole
tramite "Declare...." come farei per le normali dll di Windows.
Come posso fare per arrivare a questo risultato?
Ciao e grazie
-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 Come posso fare per arrivare a questo risultato?
Non lo fai, non con le normali (e supportate) funzionalita'
di visual basic, almeno.
Ho letto di workaround che permetterebbero di farlo, ma non
li ho assolutamente mai presi in considerazione per software
che deve girare in ambienti di produzione.
-- 
ciao, hal

> Il mio problema и questo: vorrei poter mettere le mie funzioni

Tempo fa lessi e archiviai un articolo che trattava il tema, effettivamente 
и possibile farlo, ma le uniche comoditа che otterresti sarebbero non essere 
costretto a registrare la dll e poter usare i metodi della dll anche da 
ambienti esterni il vb, anche se sulla seconda non ci giurerei... per cui il 
gioco per me non vale la candela.
comunque per chi fosse interessato ad approfondire la cosa.
Esportare funzioni in una DLL con VB
a cura di Antonio Giuliana (requisiti: voglia d'avventura)
Premessa
Si sa che con VB и possibile realizzare DLL Activex, che espongono oggetti 
COM dei quali possiamo usare metodi.
Quel che non tutti sanno и che и possibile realizzare anche DLL vere e 
proprie, che espongano funzioni, come quelle che usiamo quando dichiariamo 
API.
In questo articolo voglio presentare una semplice metodologia che puт essere 
usata per realizzare tramite Visual Basic 6, in maniera abbastanza semplice, 
delle DLL di quest'ultimo tipo.
Il metodo usato non и molto complesso e non presuppone chissа quali 
conoscenze o funzioni non documentate, ma и funzionale ed affidabile.
Esportare ed esporre
Nel titolo uso 'esportare', nel primo paragrafo uso 'esporre'; per evitare 
confusioni, chiarisco: cambia il punto di vista, il soggetto dell'azione: il 
programmatore 'esporta' una funzione, la libreria prodotta 'espone' quella 
funzione.
Forse non и inopportuno spiegare cosa si intende per 'esportare' funzioni in 
una dll: significa informare il compilatore che quelle funzioni sono 
disponibili all'esterno. Il codice binario conterrа una tabella con gli 
indirizzi dei punti di ingresso (entry point) di ciascuna funzione (assieme 
ad una tabella dei nomi, ma il senso и che applicazioni esterne 'sanno' 
quali funzioni sono disponibili e dove). Assomiglia un po' alla 
dichiarazione Public di un metodo VB.
Se una funzione del codice che forma la libreria non и in questa tabella, 
essa non и 'esportata'. Assomiglia un po' alla dichiarazione Private di un 
metodo VB.
Analogamente, una funzione puт far uso di un'altra funzione presente in 
un'altra libreria (ad esempio user32.dll), e quindi si parla di 'importare' 
funzioni in una dll: il compilatore compilerа anche una tabella delle 
funzioni 'esterne'. Assomiglia un po' alla istruzione Declare di VB.
La compilazione di un programma VB, dietro le quinte.
Per capire il metodo, bisogna partire da quello che succede quando 
compiliamo un progetto VB, cioи da quali fasi sono necessarie per convertire 
il nostro progetto in un eseguibile (o una DLL).
Dopo aver scritto il nostro sorgente nell'IDE, tramite il consueto comando 
Crea eseguibile del menu File (e a condizione di usare il tipo di 
compilazione in codice nativo), per ogni modulo (anche di classe) o form 
viene richiamato il compilatore C2.EXE che li trasforma in file oggetto.
Ad esempio, per un progetto contenente un file Form1.frm e un file 
Modulo1.bas, vengono prodotti i corrispondenti file Form1.obj e Modulo1.obj 
che contengono il codice corrispondente in linguaggio macchina (con le 
opportune chiamate alla libreria MSVBVM60.DLL).
Di questi file non rimane normalmente traccia in quanto sono temporanei e 
cancellati alla fine del processo di compilazione.
Il compilatore C2 и una versione customizzata del compilatore rilasciato con 
Visual C e non viene usato per il p-code.
Solo per curiositа, C2 viene eseguito come un processo multithreaded a 32 
bit di tipo console ed и cosм denominato in quanto rappresenta il secondo 
passo del processo di compilazione detto, appunto, a due passi; il primo 
passo и responsabilitа del codice contenuto in VB6.EXE.
L'IDE passa al compilatore sulla riga di comando alcuni parametri, in base 
al tipo di progetto prescelto, ad esempio (tutto su una riga):
  C2 -il C:\WinNT\Temp\VB613390 -f  Form1 -W3 -Gy -G5 -Gs4096 -dos -Z1
  -FoC:\ProgettoTest\Form1.obj -Qifdiv -ML -basic
in cui i vari switch hanno (probabilmente) il seguente significato:
-il nomefile il nome del prefisso usato per i nomi dei file contenente un 
'precompilato', una sorta di intermediate code (da cui il nome dello switch) 
temporaneo; questi file sono 5 e terminano con i suffissi GL, SY, EX, IN e 
DB; il loro contenuto non и documentato
-f Form1 il nome del file da compilare
-W3 warning level 3, livello di 'attenzione' da dedicare ai warnings
-Gy abilita il collegamento a livello di funzione (function-level linking)
-G5 ottimizzazione per il Pentium
-Gs4096 permette di non inserire il codice per il controllo dello stack 
(stack probe) se una funzione non usa piщ di 4096 byte di stack
-dos non documentato
-Z1 rimuove il nome della libreria di default dal file .OBJ
-Fofileobj il nome del file OBJ da generare (file output)
-Qifdiv mette in atto le correzioni per il bug della divisione del Pentium 
(FDIV bug)
-ML crea un file eseguibile single-threaded
-basic indica al compilatore C2 il fatto che la compilazione avviene per un 
progetto basic
La fase di link
Tutto ciт puт essere molto interessante da conoscere, ma и quanto succede in 
seguito la parte che ci serve.
Dopo la compilazione - come avveniva del resto sin dai tempi dei primi 
compilatori - viene eseguito il programma LINK.EXE, il cosiddetto linker, 
che provvede, essenzialmente, a collegare tutti i file oggetto facenti parte 
del progetto e a creare un singolo eseguibile risolvendo tutte le chiamate 
tra i vari moduli.
I dettagli dell'operazione sono abbastanza complessi e presuppongono delle 
conoscenze particolari dell'architettura del sistema che vanno al di lа 
degli scopi di questo articolo.
Quello che ci interessa и esaminare cosa succede quando il programma 
LINK.EXE viene chiamato - in genere al termine della trasformazione in .OBJ 
di ogni componente del progetto.
Sempre l'IDE, come per il C2, esegue il programma LINK.EXE passando nella 
linea di comando una serie di parametri, quali ad esempio (tutto su una 
riga):
  LINK C:\Test\Form1.obj C:\Test\Modulo1.obj C:\Test\Progetto1.obj 
C:\Programmi\Microsoft Visual
  Studio\VB98\VBAEXE6.lib /ENTRY:__vbaS /OUT:C:\Test\Progetto1.exe 
/BASE:0x400000
  /SUBSYSTEM:WINDOWS,4.0 /VERSION:1.0 /DEBUG /DEBUGTYPE:CV /INCREMENTAL:NO 
/OPT:REF
  /MERGE:.rdata=.text /IGNORE:4078
in cui gli switch sono tanti e non ci interessano tutti. И importante solo 
notare che sono presenti tutti i nomi dei file .Obj compilati in precedenza 
e anche il nome della libreria VBAEXE6.lib che possiamo esaminare - in una 
finestra Dos - tramite il Library Manager
  LIB /LIST VBAEXE6.LIB
che ci restituisce (almeno nella versione di VB6 da me installata)
  d:\vbadev\R6W32ND\presplit\vbarun\obj\natsupp.obj
Tale modulo oggetto non contiene codice ma solamente una tabella di salto 
statica, probabilmente utilizzata dal runtime (notate il percorso 
'originale' in cui Microsoft ha sviluppato il modulo ...)
Lo switch /ENTRY:__vbaS и importante in quanto informa il linker che il 
punto di partenza del progetto corrisponde a quello del simbolo __vbaS, 
punto definito all'interno del file Progetto1.obj, mentre lo switch 
/BASE:0x400000 indica al linker che l'indirizzo (virtuale) di base 
dell'eseguibile и quello indicato e - se possibile - sarа caricato ed 
eseguito a partire da tale indirizzo.
Per una DLL tale switch diventa /BASE:0x10000000 in quanto le DLL - sempre 
se possibile - sono caricate a partire da tale indirizzo.
Nel caso in cui il linker dovesse compilare una DLL e non un eseguibile, 
sarebbe compreso anche un ulteriore switch
  /DLL
dal significato evidente.
Il metodo.
E fin qui nulla di particolare, in quanto queste informazioni sono 
abbastanza note. И altrettanto noto il fatto che quando viene prodotta una 
DLL 'normale', tutte le funzioni non esplicitamente esportate (con la 
direttiva dllexport nel codice, o analoga) che si vogliano rendere 
disponibili agli altri programmi, possono essere elencate in un file di 
testo apposito con estensione .def (definition file).
Per esportare tali funzioni quindi и sufficiente indicare al linker il nome 
del file tramite lo switch
  /DEF:nomefiledef
E questo basterebbe, ma bisogna farlo nella fase in cui l'IDE richiama il 
LINK al momento della creazione dell'eseguibile.
Per fare ciт si potrebbe agire con un addin (come in alcuni esempi и stato 
fatto) che intervenga durante il processo di compilazione, ma questo и un 
metodo piuttosto 'delicato' e inutilmente complesso per quello che si vuole 
ottenere.
Il metodo che propongo, invece, sfrutta il fatto che il programma LINK.EXE 
richiamato dall'IDE durante la compilazione и presente nella cartella del 
VB, in cui и presente anche il compilatore C2.
L'idea и quindi sostituire questo eseguibile con un nostro eseguibile 
scritto in VB (!) chiamato LINK.EXE dopo aver rinominato il vecchio 
programma in LINK32.EXE (consiglio di copiare per sicurezza il file LINK.EXE 
originale in un'altra cartella di backup per evitare di "perderlo" per 
errore).
Avremo cosм nella cartella del VB due file
. LINK.EXE - un eseguibile scritto in VB richiamato durante la fase di link 
di qualsiasi progetto
. LINK32.EXE - l'eseguibile originale del linker richiamato dal nuovo 
programma LINK.EXE
Quando verrа richiamato il nuovo LINK, questo dovrа esaminare tutti i 
parametri passati nella linea di comando, riconoscere il fatto che si sta 
compilando una DLL in VB6 (tramite lo switch /DLL e la presenza del file di 
input VBAEXE6.LIB) ed aggiungervi lo switch /DEF con il nome del file di 
definizione avendo cura di controllare preventivamente la presenza di tale 
file.
Esaminando il codice del nuovo programma LINK.EXE, si potrа capire meglio:
  ' File LINK.EXE
  Public Sub Main()
    Dim cmd As String
    Dim fOut As Long
    Dim sOut As String
    Dim sDef As String
    cmd = Command$
    If InStr(cmd, "/DLL ") > 0 And InStr(cmd, "VBAEXE6.LIB") > 0 Then
      fOut = InStr(cmd, "/OUT:")
      sOut = Mid(cmd, fOut + 6, InStr(cmd, "/BASE:") - fOut - 8)
      sDef = Left(sOut, Len(sOut) - 3) + "def"
      If Len(Dir(sDef)) Then
        cmd = cmd & " /DEF:""" & sDef & """"
      End If
    End If
    Shell "LINK32.EXE " & cmd
  End Sub
Tale programma и costituito solamente da una breve procedura Main in un 
modulo che preleva il contenuto della linea di comando e la esamina. Se 
contiene lo switch /DLL e il file di input VBAEXE6.LIB, continua con l'esame 
della stringa in input altrimenti richiama semplicemente il vecchio LINK 
(adesso denominato LINK32) passandogli in modo trasparente la linea di 
comando originale. Ciт garantisce la "non interferenza" con le compilazioni 
'normali'.
Se viene rilevata la compilazione di una DLL di VB6, allora viene creata una 
stringa con lo stesso nome del file presente nello switch /OUT ma con 
estensione .DEF. Se tale file esiste, viene aggiunto lo switch /DEF alla 
linea di comando e passato al vecchio LINK (LINK32). Ciт indicherа al 
vecchio LINK che esiste un file con un elenco di funzioni da esportare che 
saranno quelle che ci interessa rendere disponibili.
Un esempio e il contenuto del file .DEF
Per completare, ecco un esempio, scaricabile dall'Area Download - si suppone 
che il vecchio LINK.EXE presente nella cartella di VB (in genere, 
\Programmi\Microsoft Visual Studio\VB98) sia stato rinominato in LINK32.EXE 
e che il nuovo LINK.EXE sia stato copiato in tale cartella.
Una precisazione и d'obbligo. Dato che l'IDE (VB6.EXE) effettua il primo 
passo della compilazione e controlla tutti gli switch passati al compilatore 
e al linker, и necessario creare un progetto di tipo DLL, con un modulo di 
classe praticamente vuoto utilizzato solamente allo scopo di ingannare 
l'ambiente e fare in modo che predisponga i corretti switch per quella che 
comunque sarа una DLL. Ciт non vuol dire che dovremo registrare tale DLL 
(anche se le funzioni per la registrazione/deregistrazione saranno esportate 
e disponibili) e potremo usare solamente le funzioni esportate che ci 
saranno alla fine del processo.
L'esempio (MyDLL.vbp) и un progetto costituito da un modulo di classe 
(CDummy.cls) completamente vuoto ed incluso solo per i motivi giа esposti, e 
da un normale modulo (SymExp.bas) contenente le funzioni che si vogliono 
esportare. Nell'esempio che ho scritto queste sono due e precisamente:
  Public Function AddTest(ByVal op1 As Long, ByVal op2 As Long) As Long
    AddTest = op1 + op2
  End Function
  Public Function StrRevTest(ByVal s As String) As String
    Dim r As String
    r = StrConv(s, vbUnicode)
    r = StrReverse(r)
    StrRevTest = StrConv(r, vbFromUnicode)
  End Function
e, in piщ, и presente una funzione Main vuota
  Private Sub Main()
  End Sub
utilizzata solamente perchй il progetto impone tale presenza formale.
Naturalmente sono semplici esempi didattici, ma ritengo opportuno farvi 
notare che i parametri ed i valori di ritorno sono tipizzati in tipi base 
(vedi anche Conclusioni).
И importante anche notare, per quanto riguarda il passaggio delle stringhe, 
che esse devono essere convertite in ingresso/uscita rispetto al formato 
Unicode; a questo provvede la funzione StrConv (nei due sensi).
Una volta effettuata la compilazione, otterremo il nostro solito file 
MyDll.Dll che perт, esaminato con il Dependency Walker, avrа questo 
contenuto
in cui risulta evidente la presenza delle funzioni AddTest e StrRevTest tra 
quelle esportate ed utilizzabili!
Come detto, ciт и stato reso possibile dalla presenza del file .def che deve 
contenere l'elenco delle funzioni da esportare ma, a questo punto, и 
necessaria una precisazione.
Dato che il compilatore (C2, il secondo passo) и in effetti una variante di 
quello del C++, i simboli risultano 'decorati' secondo un preciso criterio e 
quindi и necessario scrivere quanto segue nel file .def del nostro esempio 
(MyDll.def)
  LIBRARY MyDLL
  DESCRIPTION "MyDLL - (C) Antonio Giuliana, 2004"
  EXPORTS
    AddTest = ?AddTest@SymExp@@AAGXXZ
    StrRevTest = ?StrRevTest@SymExp@@AAGXXZ
Mentre le prime due righe hanno un chiaro significato, la terza indica che 
inizia l'elenco delle funzioni esportate e che il nome delle funzioni 
all'interno del progetto in VB corrisponde a quello 'decorato' posto a lato 
del simbolo di eguaglianza.
In generale, il nome decorato и composto da ?NomeFunzione@NomeModulo@@AAGXXZ 
(non chiedetemi cosa indichi AAGXXZ... ).
Utilizziamo la DLL in un progetto VB
Concludiamo con un piccolo esempio che utilizza la DLL che abbiamo creato 
(premettendo il fatto che la DLL ottenuta и stata semplicemente copiata - 
non registrata - all'interno di C:\Winnt\System32 o altra cartella di 
sistema)
  Option Explicit
  Private Declare Function AddTest _
    Lib "MyDLL.DLL" _
    (ByVal op1 As Long, ByVal op2 As Long) As Long
  Private Declare Function StrRevTest _
    Lib "MyDLL.DLL" _
    (ByVal s As String) As String
  Public Sub Main()
    MsgBox AddTest(189, 120)
    MsgBox StrRevTest("CIAO")
  End Sub
Come и facile vedere, le due funzioni esportate sono state dichiarate come 
normali API ed utilizzate di conseguenza, molto semplicemente.
Conclusioni
Naturalmente non ho fatto tutte le prove del caso riguardo al tipo di 
parametri passabili e valori restituibili, ma tenete presente che le 
librerie di funzioni, in genere, accettano tipi semplici mentre, per quelli 
complessi, in genere si passa un riferimento (un puntatore).
-- 
"Se la conoscenza puт creare dei problemi, non и con l'ignoranza che
possiamo risolverli" Isaac Asimov
Per bypassare i filtri antispam scrivi nel testo del messaggio "AIEIE"
MasterDavidex - David Ruscelli
ICQ 223258511 

 Ciao e grazie
A parte che le DLL standard (quelle che esportano API) tu non necessitano di
registrazione, quello che puoi fare in modo semplice con VB6 č creare una DLL
ActiveX/COM che espone una classe Global Multiuse che ha per metodi tutte le
funzioni che ti servono.
Questa sě che necessita di registrazione.
Attento al cosiddetto "DLL Hell"... prima di cimentarti in questo lavoro leggiti
bene ad es. questo articolo:
http://www.microsoft.com/msj/0100/versioning/versioning.aspx
Bye
Raf

 gioco per me non vale la candela.
[CUT]
Vi segnalo un interessante addin per VB che sembrerebbe realizzare questa cosa
in modo relativamente semplice.
http://www.vbadvance.com/
Bye
Raf
P.S. Mi sa che hai l'orologio del PC avanti di qualche minuto...

 a cura di Antonio Giuliana (requisiti: voglia d'avventura)
Per completezza, articolo pubblicato su 
www.visual-basic.it
 con tanto di 
codice di esempio a corredo.
Saluti.
-- 
Gianluca Cannalire
Microsoft MVP/MCP - Visual Basic
http://www.visual-basic.it
http://community.visual-basic.it/gianluca