Utilisation des dll -delphi raw
programming- Par Clandestino
/ Fighting For Fun
type de sujet : coding
Niveau requis
: |
X
|
X
|
X
|
X
|
X
|
Beginner |
Introduction
Le
but de ce tutoriel est de vous familiariser avec l'utilisation des
dlls, mais du point de vue du programmeur. Des exemples concrets
valant mieux, a mon sens, que de longs discours,ce tut est donc surtout
compose de codes sources largement commentes pour en faciliter l'etude. J'empreinte
le terme 'Delphi Raw Programming' a des auteurs anglo-saxons
pour designer des techniques de programmation permettant (entre
autres choses) de creer des executables de petite taille.
Delphi
Raw Programming (DRP): - Le langage de developpement utilise est
le pascal et surtout l'API window. - Le compilateur utilise est
dcc32.exe de borland inclus dans Borland Delphi ou Borland C++. -
On evite les vcls et composants delphis tout prets pour construire
les interface 'a la main'. Les projets peuvent etre developpes avec
un simple editeur de texte (le bloc note suffit) et compilee par
ligne de commande...
C'est dans ce cadre, loin de tout assistant
ou interface prefabriquee, que je vous propose de plonger, avec l'espoir
que le savoir que je partage ici sera utile a beaucoup.
|
Creation d'une dll
Avec Delphi, le code source d'une dll est similaire à celui d'un programme, c'est un fichier
texte avec l'extension *.dpr. La principale différence réside dans le mot clef
de début de code source : Library au lieu de program La structure type
est la suivante : |
Library nom_de_la_dll
; { Le mot clef library indique au compilateur
de compiler une dll au lieu d'un exe. Il est suivi du nom_de_la_dll
qui est aussi celui de ce fichier : nom_de_la_dll.dpr }
Uses Unit1, Unit2 ; {
le mot clef Uses sera suivi des unites (*.pas ou *.dcu) que vous
voulez integrer pour la compilation... Par exemple : Uses
windows, Msg ; }
{ $R
nom_fichier_ressource.res }
{ Utilisez cette syntaxe pour intégrer des resources précompilées (*.res)
à votre dll }
Var Variable1
: Type1 ; Variable2 : Type2
; { Definissez vos variables apres le
mot clef Var ou vos constantes apres le mot clef Const
}
Procedure Nom_de_la_Procedure(var1
: Type1, var2 : Type2...); stdcall ; begin {
corps de la procedure } end; {
Definissez vos Procedure
de maniere classique sans oublier de specifier la convention d'appel de dll utilisée
: ici stdcall pour etre compatible avec C++ ou l'asm...
Explication : par defaut delphi utilise la convention register qui
passe les parametres dans le sens inverse des autres conventions... Pour
créer des dll compatibles avec d'autres langages, n'oubliez pas de spécifier
stdcall }
Function Nom_de_la_Fonction(var1
: Type1, var2 : Type2...): type3; stdcall ; begin
{ corps de la fonction } result := ...
{ n'oubliez pas d'attribuer un resultat (ici de type3) à votre fonction
} end; {
Définissez vos fonctions de
manière classique sans oublier de spécifier la convention d'appel comme pour les
procédures }
Exports Nom_de_la_procedure, Nom_de_la_fonction; {
Précisez
après le mot clef exports le nom des fonctions et des procedures
exportées par votre dll. }
begin
{ corps de la dll : le code placé ici sera utilisé lors de l'initialisation
de cette dernière... } end.
|
Attention : - Choisissez un nom de dll personalisé (distinct des noms
de dll utilisées par windows) car un meme programme ne peut charger deux
dll ayant le meme nom.
- Soyez vigilents sur l'utilisation de la memoire
: n'oubliez pas de desalouer cette dernière par votre dll
si celle-ci l'a allouée
|
Exemple de dll vide
Creez un fichier texte et renommez-le madllvide.dpr Copiez-y
le code source suivant :
|
Library madllvide
; Uses windows ; begin
Messagebox(0,'Message','dll
chargée en mémoire',MB_OK) ; {
Ce message s'affichera lors du chargement de la dll } end.
|
Exemple de Dll de ressources :
Nous allons creer une dll contenant une image. Creez un dossier vide et creez-y un fichier texte avec Picture.rc.
Copiez
dans ce dossier la bitmap qui sera contenue en tant que ressource et nommez-la
image.bmp NB J'ai utilise dans mon exemple une image de 202x76 pixels enregistre
avec photoshop en mode 8 bits / couleurs indexees pour gagner en
taille...
Code source de Picture.rc
|
Compilation 'à la main' d'un fichier resource: Copiez
dans votre dossier le fichier brcc32.exe contenu dans le repertoire 'bin'
de votre delphi puis faites glisser Picture.rc à la souris sur brcc32.exe
pour compiler votre fichier ressource et obtenir Picture.res, ou bien utilisez
brcc32 en ligne de commande...
Creation de la dll de ressources qui va contenir
cette image Le code est simplissime, il suffit d'indiquer au compilateur
qu'il faut inclure le fichier Picture.res
Code source de dllresources.dpr
|
library dllresources
; {$R Picture.res}
begin
end.
|
Exemple simple de programme utilisant cette
Dll de ressources :
Nous allons creer une fiche qui va charger dynamiquement
l'image contenue dans la dll que nous venons de compiler, le resultat final
devant resembler a cela :
Je parts du principe
que vous savez creer une interface avec delphi et l'API windows... Un cours
sur ce sujet (niveau newbies) sera publié...( En attendant, un exemple de fenetre
vide est inclus dans minidelphi...) Creez un fichier texte nomme
fenbitmap.dpr, et codez-y une fenetre classique windows. Nous ne gererons pour
cet exempleque les messages WM_CREATE et WM_DESTROY Pour appeler dynamiquement
une dll il faut : - la charger en memoire grace à la commande
loadlibrary('Nom_de_la_dll') - charger les ressources
incluses grace à une commande appropriée : loadbitmap(handle_de_la_dll,'nom_de_la_ressource')
ou bien loadicon, loadcursor, loadstring, loadressource...
Voir l'aide de l'API windows pour plus de details... - trouver
l'adresse d'une procedure ou d'une fonction pour l'utiliser grace à
la commande GetProcAddress(handle_de_la_dll, 'nom_de_la_fonction')
- Libérer la mémoire avec freelibrary(handle_de_la_dll)
Code source de fenbitmap.dpr
|
Program fenbitmap; Uses Windows,
Messages;
{ les unites utilisees pour la compilation } Var
{
les variables } WinClass:
TWndClassA; Inst: HINST; hWindow:
HWND; TheMessage: TMsg; HandleDll
: hwnd; himage
: hbitmap;
// GESTION DES MESSAGES function
WindowProc(hWindow: HWnd; Msg,wParam,lParam: Integer): Integer; stdcall; begin
Result := 0; case Msg of
{ Avant la
creation de la fiche } WM_CREATE
: begin {
chargement de la dll } HandleDll
:= Loadlibrary('DllResources.dll')
; {
en cas d'echec de chargement } if
HandleDll = 0 then begin messagebox(0,'Attention','Dll
non trouvée', MB_OK) ; Postquitmessage(0); end
{
sinon } else
begin himage
:= loadbitmap(handledll,pchar('MonImage'));
{ chargement de l'image }
{
on cree une image vide sur notre fenetre window } CreateWindow('Static','',
WS_VISIBLE or WS_CHILD or SS_BITMAP, 32,18,
// coordonnees / droite et
haut 202,76,
// largeur hauteur
hWindow,
0, Inst, nil); {
on y applique l'image chargee } SendMessage(Image1,STM_SETIMAGE,IMAGE_BITMAP,himage); end;
{
On libere la dll } FreeLibrary(HandleDll); end;
{ Avant la
destruction de la fiche }
WM_DESTROY : begin deleteobject(himage)
; { destruction de l'iimage } postquitmessage(0)
; { fermeture propre }
end; ELSE Result
:= DefWindowProc(hWindow, Msg, wParam, lParam); end; end;
// PROGRAMME PRINCIPAL begin {
enregistrement WndClass } Inst
:= hInstance; with WinClass do begin style
:=
CS_CLASSDC or CS_PARENTDC; lpfnWndProc
:= @WindowProc; hInstance
:=
Inst; hbrBackground :=
color_btnface + 1; { Astuce : pour une
couleur personnalisee du fond utiliser createsolidbrush(RGB(0,0,0))
avec les chiffres de votre choix } lpszClassname
:= 'MyWindowClass'; hCursor
:=
LoadCursor(0, IDC_ARROW); end; {
du with }
RegisterClass(WinClass);
{ Creation de la fenetre } {
utilisez l'aide sdk window incluse avec delphi si vous voulez des détails sur
les differents styles disponibles avec createwindowex } hWindow
:= CreateWindowEx(WS_EX_DLGMODALFRAME, {
style general de la fenetre (non redimensionnable)} 'MyWindowClass',
{
le nom de votre wndclass } 'Image
dans dll', //
titre de la fenetre WS_VISIBLE
or WS_CAPTION or WS_SYSMENU, {
styles particuliers de la fenetre (visible,titre et bouton)} round(getsystemmetrics(SM_CXSCREEN)/2)-139,
{ distance/gauche
de votre ecran ici on centre la fiche en prenant le milieu de l'ecran
moins la demi largeur } round(getsystemmetrics(SM_CXSCREEN)/2)-70,
{ distance /haut
de votre ecran } 278,
{
largeur de votre fenetre (proportion choisie pour une bitmap de 202 pixels)
} 140,
{
hauteur de votre fenetre (pour une bitmap de 76 pixels) } 0,
{
handle de la fenetre parente : ici laisser 0 } 0,
{
handle du menu ou d'une fenetre fille : ici laisser 0 } Inst,
{
handle de cette instance de l'application } nil);
{
pointeur vers les datas de création de cette fenetre laisser nil } UpdateWindow(hWindow);
{La boucle principale} while
GetMessage(TheMessage,0,0,0) do begin if
not IsDialogMessage(hWindow,TheMessage) then begin TranslateMessage(TheMessage); DispatchMessage(TheMessage); end; end; end.
|
Compilez cet exemple dans delphi, ou en utilisant
le compilateur dcc32 (contenu dans le repertoire bin de votre delphi)
à la souris en glissant votre fichier source sur dcc32.exe ou bien
avec minidelphi.
|
Exemple de Dll de fonction :
Nous allons creer une dll contenant une fonction. J'ai
choisi une fonction de génération de clef (celle d'un exercice du REA)
Code source de madllfonction.dpr
|
library madllfonction; {
le nom de notre dll } {
pas besoin de spécifier d'uses ni de variables globales
pour cette dll }
function calculserial(name :pchar):pchar; stdcall;
{ La fonction que l'on
veut exporter } {
je n'entrerai pas dans les détails pour expliquer le fonctionnement
de cet algorithme simplissime, ce n'est pas le but de ce tutoriel...
disons simplement que cette fonction prend un pchar, fait quelques
opérations dessus, et renvoie le resultat sous forme de pchar }
var
{
les variables utilisees par la fonction } b,i
: integer ; s, texte : string ; begin {
debut } texte
:= '' ; s := name ; getmem(result,
11) ; for i:= 1 to length(s) do begin if i<11 then begin b
:= ord(s[i]); b := b mod 10 ; b
:= b XOR (i-1) ; b := b+2 ; if b>9 then b
:= b-10 ; b := b+50 ; if b>57 then b
:= b-10 ; Texte := texte+chr(b)
; end ; end ; Texte
:= Texte+#0 ; result := pchar(Texte)
; { le resultat sous forme
de pchar} end
; { fin de la fonction }
Exports
{ la partie qui stipule les
exports } calculserial;
begin {
code au chargement de la dll, ici rien... } end.
|
Utilisation dynamique de cette
Dll de fonction :
Nous allons creer une fiche contenant deux champs
de saisie et un bouton. Lorsqu'on appuiera sur le bouton, on chargera
la dll (et sa fonction) pour calculer à partir de la valeur du champ
de saisie1, le serial généré que l'on placera dans le champs de
saisie2. En bref : nous allons creer un keygen avec son algorithme
dans une dll...
Code source de MonGenerateur.dpr
|
Program MonGenerateur; Uses Windows, Messages; Var WinClass: TWndClassA; Inst: HINST; hWindow, memo1, memo2, button1: HWND;
{ handles : fenetre,
champ1, champ2, bouton } TheMessage: TMsg; HandleDll : hwnd;
{ handle de la dll } calculserial : function (name:pchar): pchar;stdcall;
{ procedure
liee au click sur le bouton} procedure click;
var nom : pchar; longueurtexte : integer; begin { Je
charge ma dll } HandleDll := Loadlibrary('madllfonction.dll'); { en
cas d'erreur :} if HandleDll = 0 then begin messagebox(0,'Attention','Dll
non trouvée', MB_OK); Postquitmessage(0); end; longueurtexte := GetWindowTextLength(memo1); { Si
on a ecrit quelque chose ...}
if longueurtexte > 0 then begin GetMem(nom, longueurtexte
+1); { memoire pour
le nom } GetWindowText(memo1,
nom, longueurtexte +1); { recup
du nom en memoire } @calculserial
:= GetProcAddress(handledll,'calculserial');
{ recup de l'adresse
de la fonction } setwindowtext(memo2,calculserial(nom)); { affichage
du resultat dans le champ de saisie 2 } FreeLibrary(HandleDll);
{ liberation de
la dll } freemem(nom);
{ liberation de
la memoire (du nom) } end
{ Sinon
} else setwindowtext(memo2,'');
{ on efface le
champ de saisie 2 } end;
{ gestion
des messages : interception } function WindowProc(hWindow: HWnd; Message,wParam,lParam: Integer): Integer;
stdcall; begin Result := 0; case Message of { en
cas de fermeture } WM_DESTROY: begin postquitmessage(0); end; { En
cas de
commande } WM_COMMAND: begin if (lParam = Button1) then click;
{ Si la commande
vient du bouton, j'appelle la procedure click } end; { Autres
cas } ELSE Result := DefWindowProc(hWindow, Message, wParam,
lParam);
end; end;
begin Inst := hInstance; with WinClass do begin style
:=
CS_CLASSDC or CS_PARENTDC; lpfnWndProc :=
@WindowProc; hInstance :=
Inst; hbrBackground := color_btnface
+ 1; lpszClassname := 'MyWindowClass'; hCursor :=
LoadCursor(0, IDC_ARROW); end; RegisterClass(WinClass);
{ Creation de la fenetre } hWindow := CreateWindowEx(WS_EX_DLGMODALFRAME,'MyWindowClass','Fonction
dans dll', WS_VISIBLE
or WS_CAPTION or WS_SYSMENU, round(getsystemmetrics(SM_CXSCREEN)/2)-130, round(getsystemmetrics(SM_CXSCREEN)/2)-60, 160,120,0,0,Inst,nil);
{ Creation du champ
de saisie1 } Memo1:= CreateWindowEx(WS_EX_CLIENTEDGE, 'Edit','', WS_CHILD
or WS_VISIBLE or WS_BORDER or
ES_LEFT,5,5,140,25, hWindow, 0, Inst, nil); { Creation du
champ de saisie2 } Memo2:= CreateWindowEx(WS_EX_CLIENTEDGE, 'Edit','', WS_CHILD
or WS_VISIBLE or WS_BORDER or
ES_LEFT,5,35,140,25, hWindow, 0, Inst, nil); { Creation du
bouton} Button1 := CreateWindow('Button', 'Generer serial', WS_VISIBLE
or WS_CHILD or BS_TEXT, 6, 65,
138, 24, hwindow, 0, Inst, nil);
UpdateWindow(hWindow);
while GetMessage(TheMessage,0,0,0) do begin if not IsDialogMessage(hWindow,TheMessage) then begin TranslateMessage(TheMessage); DispatchMessage(TheMessage); end; end; end.
|
Comment compiler un code source DRP :
Si vous avez Delphi : Les codes sources
donnes ici peuvent directement etre compiles dans l'interface de Delphi
si vous le desirez. Vous
pouvez aussi compiler un code source en faisant un drag and drop a la
souris de votre fichier sur le compilateur dcc32.exe que vous trouverez
dans le repertoire bin de borland Delphi ou de Borland C++ Vous pouvez
aussi lancer dcc32 en ligne de commande dans une console pour compiler votre
projet, vous aurez ainsi les message de reussite ou d'erreur du compilateur...
La syntaxe a utiliser
: dcc32 [options] fichier [options]
-A<unité>=<alias>
= Alias d'unité -B
= Construire toutes les unités -CC = Cible console -CG = Cible GUI -D<syms> = Définir conditionnels -E<chemin>
= Répertoire dest. EXE -F<offset>
= Chercher erreur -GD = Fichier map détaillés -GP = Fichier map avec publics -GS = Fichier map avec segments -H
= Afficher les conseils -I<paths> = Répertoires
d'inclusion -J = Générer fichier
.obj -JP = Générer fichier .obj
C++ -K<addr>
= Adresse de base image | -LU<paquet> = Utiliser paquet -M = Construire unités
modifiées -N<chemin>
= destination DCU -O<chemins>
= Répertoires objets -P = Chercher aussi fichiers 8.3 -Q = Compilation silencieuse -R<chemins>
= Répertoires ressources -U<chemins>
= Répertoires unités -V
= Infos débogage dans EXE -VR = Générer débogage distant (RSM) -W
= Afficher les avertissements -Z = Générer DCPs -$<dir>
= Directive de compilation --help = Affiche cet écran d'aide --version = Affiche
le nom et la version |
Options du compilateur : -$<lettre><état>
(voir défauts ci-dessous)
A8 Champs enregistrements
alignés B-
Evaluation booléenne complète C+ Evaluer
assertions à l'exécution D+ Informations de débogage G+ Utiliser réf. données
importées H+ Utiliser chaînes longues par défaut I+ Vérification
E/S J- Consts structurées
en écriture L+
Symboles débogage locaux M- Info type à l'exécution O+ Optimisations
| P+ Params chaîne ouverte Q-
Vérification débordement entier R- Vérification
étendue T-
Opérateur @ typé U- Division Pentium(tm)
sûre V+
Chaîne variables strictes W-
Générer cadre de pile X+ Syntaxe étendue Y+
Info référence symbole Z1
Taille mini types énumérés
|
|
Compilation avec minidelphi :
Pour compiler un projet avec minidelphi il suffit
de choisir dans le menu outils le sous menu compiler :
|
|