Un code simple démontrons l'implémentation d'une classe COM dans une DLL.
Pour le compiler il vous faudra le Visual Studio car il nécessite MIDL pour compiler le fichier .IDL.
Le composant peut-être testé très facilement en appelant CoCreateInstance d'une source C++.
Je ne connais pas VB mais on dit qu'il ets très simple d'y incorporer des composants COM. Si quelqu'un sait comment faire, je me ferais un plaisir d'y mettre sa méthode.
L'ajout de fonctionnalités au composant se fait de manière quasi-intuitive (tout est relatif ;p). Il suffit de rajouter une entrée dans la VMT (il ne faut pas oublier le fichier .IDL).
note : la source ne comprend pas la routine de désenregistrement du composant et elle ne sera pas mis à jour pour l'incorporer (manque de temps et pour moi ce n'est pas primordial étant donné que je travail toujorus avec le même CLSID).
note 2 : le fichier .RC est fourni mais il vous faut compiler le fichier .IDL pour obtenir la TypeLib (.TLB)
note 3 : la présentation est laide sur le site mais pas dans la source donc downloadez le ZIP pour plus de clarté dans la lecture !
Source / Exemple :
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Basic COM unique class implementation ;;
;; ;;
;; auteur : gblade/Garus Blade ;;
;; ;;
;; description : bases d'un élément COM programmé en pur ASM ;;
;; nous n'étudierons ici que les éléments uniques/statics ;;
;; ;;
;; note : la routine de désenregistrement n'est pas implémentée ;;
;; il est à votre charge de nettoyer le registre ;;
;; ou plus simplement d'écrire cette procédure par vous-même ;;
;; ;;
;; note1 : ceci est une version de debuggage ;;
;; ceci n'implique pas qu'elle ne soit pas fonctionnelle ;;
;; simplement, elle contient des élément de pistage d'erreurs ;;
;; ;;
;; note2 : les labels ne sont pas tous appelés ;;
;; ils sont présent à titre informatif pour facilité la relecture ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.386 ;nous utiliserons les instructions du 386
option casemap:none ;respect de la casse
option language:syscall ;convention de nommage SYSCALL (pas d'underscores)
option proc:private ;visibilité des procédure mis à PRIVATE (seules les les fonctions exportées seront déclarées publiques)
option prologue:none ;génération manuelle du code de prologue
option epilogue:none ;génération manuelle du code d'épilogue
assume cs:flat,ds:flat,ss:flat,es:flat,fs:nothing,gs:nothing ;model de segmentation FLAT (segment unique, model des modules win32)
;****************************************
;***** section importations (début) *****
;** il est conseillé d'utiliser les en-têtes prédéfinis (kernel32.inc, user32.inc, etc...)
;** les fonctions sont incluses ici manuellement pour une identifications plus rapide de l'apartenance à une librairie
;** les procédures importées sont classées par apartenance aux librairies
extrn _GetModuleFileNameA@12:near
GetModuleFileName equ <_GetModuleFileNameA@12>
extrn _MultiByteToWideChar@24:near
MultiByteToWideChar equ <_MultiByteToWideChar@24>
includelib kernel32.lib
extrn _MessageBoxA@16:near
MessageBox equ <_MessageBoxA@16>
includelib user32.lib
extrn _IsEqualGUID@8:near
IsEqualGUID equ <_IsEqualGUID@8>
includelib ole32.lib
extrn _LoadTypeLib@8:near
LoadTypeLib equ <_LoadTypeLib@8>
extrn _RegisterTypeLib@12:near
RegisterTypeLib equ <_RegisterTypeLib@12>
includelib oleaut32.lib
extrn _RegCreateKeyA@12:near
RegCreateKey equ <_RegCreateKeyA@12>
extrn _RegSetValueA@20:near
RegSetValue equ <_RegSetValueA@20>
extrn _RegSetValueExA@24:near
RegSetValueEx equ <_RegSetValueExA@24>
extrn _RegCloseKey@4:near
RegCloseKey equ <_RegCloseKey@4>
includelib advapi32.lib
;***** section importations (fin) *****
;**************************************
;*********************************************
;***** équivalences de fonctions (début) *****
;** ce sont les équivalence entre le nom court et le nom complet des fonctions qui le requiert
DllMain equ <_DllMain@12> ;équivalence des noms court/complet de _DllMain@12
;***** équivalences de fonctions (fin) *****
;*******************************************
;**************************
;***** macros (début) *****
DEFINE_GUID macro name:req,l:req,w1:req,w2:req,b1:req,b2:req,b3:req,b4:req,b5:req,b6:req,b7:req,b8:req ;macro de définition de GUIDs
name:
dd l
dw w1
dw w2
db b1
db b2
db b3
db b4
db b5
db b6
db b7
db b8
endm
trace macro msg:req ;macro de debuggage
push 0
push offset msg
push offset msg
push 0
call MessageBox
endm
;***** macros (fin) *****
;************************
;******************************
;***** constantes (début) *****
;** il est conseillé d'utiliser windows.inc
TRUE equ 1 ;vrai binaire
FALSE equ 0 ;faux binaire
NULL equ 0000h ;adresse nulle
MAX_PATH equ 260 ;nombre de caractères maximum pour un chemin
CP_ACP equ 0 ;page de code (caractères) ANSI
DLL_PROCESS_ATTACH equ 1 ;attachement d'une DLL au même processus
HKEY_CLASSES_ROOT equ 80000000h ;clé prédéfinie HKEY_CLASSES_ROOT
REG_SZ equ 1 ;type chaîne de caractères pour une valeur de clé du registre
S_OK equ 0 ;code de retour sans erreur
S_FALSE equ 1 ;code de retour sans erreur indiquant une réponse négative de la fonction
E_FAIL equ 80004005h ;code d'erreur indiquant un échec
E_INVALIDARG equ 80070057h ;code d'erreur indiquant qu'un ou plusieurs des arguments passés à la procédure
E_POINTER equ 80000005h ;code d'erreur indiquant qu'un des pointeurs passés en argument à la procédure était invalide (avait une valeur nulle)
CLASS_E_CLASSNOTAVAILABLE equ 80040111h ;code d'erreur indiquant que la classe (CLSID) demandée n'est pas pris en charge
CLASS_E_NOAGGREGATION equ 80040110h ;code d'erreur indiquant que l'aggregation demandée n'est aps prise en charge
E_NOINTERFACE equ 80004002h ;code d'erreur indiquant que l'interface (IID) demandée n'est aps prise en charge
E_NOTIMPL equ 80000001h ;code d'erreur indiquant que la procédure demandée n'est pas prise en charge (n'est pas implémentée)
;***** constantes (fin) *****
;****************************
;*****************************************
;***** types supplémentaires (début) *****
;** ces types sont déclarés afin de facilité la lecture du code
;** il est possible d'utiliser windows.inc
handle typedef dword
pointer typedef dword
dp equ <dd>
char typedef byte
dc equ <db>
wchar typedef word
;***** types supplémentaires (fin) *****
;***************************************
;**********************************
;***** constantes POO (début) *****
;** ces constantes sont définies dans le but d'implémenter plus simplement la POO
_self equ <8[ebp]> ;pointeur équivalent du pointeur this en C++
;***** constantes POO (fin) *****
;********************************
;***************************
;***** données (début) *****
_DATA segment dword public 'DATA'
;;chaînes de caractères ASCIIs
$ProgID dc 'ProgID',0
$ProgID_MyComponent dc 'MyComponent',0
$Desc dc 'A simple COM component',0
$CLSID dc 'CLSID',0
$CLSID_MyComponent dc '{EDFC5B3D-FFE7-47b5-94D7-63BC8CA9355F}',0
$Inproc dc 'InprocServer32',0
$ThreadingModel dc 'ThreadingModel',0
$ThreadingType dc 'Both',0
;;chaînes de dubuggage
;;ces chaînes devraient être supprimées si aucune utilité n'en est faites
$dllmain dc 'DllMain',0
$dllcanunloadnow dc 'DllCanUnloadNow',0
$dllgetclassobject dc 'DllGetClassObject',0
$queryfactory dc 'QueryInterface Factory',0
$addfactory dc 'AddRef Factory',0
$releasefactory dc 'Release Factory',0
$createinstance dc 'CreateInstance',0
$lockserver dc 'LockServer',0
$queryinterface dc 'QueryInterface',0
$addref dc 'AddRef',0
$release dc 'Release',0
;;déclarations des GUIDs, les GUIDs ont été générés par uuidgen.exe de Microsoft
DEFINE_GUID CLSID_MyComponent,0EDFC5B3Dh,0FFE7h,47b5h,94h,0D7h,63h,0BCh,8Ch,0A9h,35h,5Fh
DEFINE_GUID IID_IUnknown,00000000h,0000h,0000h,0C0h,00h,00h,00h,00h,00h,00h,46h
DEFINE_GUID IID_IClassFactory,00000001h,0000h,0000h,0C0h,00h,00h,00h,00h,00h,00h,46h
DEFINE_GUID IID_IMyComponent,0EDFC5B3Eh,0ed8bh,4a9dh,0ach,0f7h,9eh,1dh,4fh,22h,0fch,90h
;;variables globales
_hDllInstance dd NULL ;handle de l'instance de la DLL
_dwGlobalCount dd 0 ;ompteur global (valeur additionnée de tous les compteurs d'objets de la DLL)
;;objets statics/uniques et VMTs
_ClassFactoryObject: ;objet static,unique ClassFactory
dp offset _IClassFactoryVMT ;pointeur sur la Virtual Methods Table (VMT) par défaut (IClassFactory)
dd 0 ;compteur de l'objet
_IClassFactoryVMT: ;Virtual Methods Table (VMT) de l'interface IClassFactory
dp QueryInterface@ClassFactory$IUnknown ;méthode virtuelle QueryInterface, héritée de IUnknown
dp AddRef@ClassFactory$IUnknown ;méthode virtuelle AddRef, héritée de IUnknown
dp Release@ClassFactory$IUnknown ;méthode virtuelle Release, héritée de IUnknown
dp CreateInstance@ClassFactory$IClassFactory ;méthode virtuelle CreateInstance de interface par défaut IClassFactory
dp LockServer@ClassFactory$IClassFactory ;méthode virtuelle LockServer de interface par défaut IClassFactory
_MyComponentObject: ;objet static,unique MyComponent
dp offset _IMyComponentVMT ;pointeur sur la Virtual Methods Table (VMT) par défaut (IClassFactory)
dd 0 ;compteur de l'objet
_IMyComponentVMT: ;Virtual Methods Table (VMT) de l'interface IMyComponent
dp QueryInterface@MyComponent$IUnknown ;méthode virtuelle QueryInterface, héritée de IUnknown
dp AddRef@MyComponent$IUnknown ;méthode virtuelle AddRef, héritée de IUnknown
dp Release@MyComponent$IUnknown ;méthode virtuelle Release, héritée de IUnknown
_DATA ends
;***** données (fin) *****
;*************************
;************************
;***** code (début) *****
_TEXT segment para use32 public 'CODE'
;;point d'entrée de la DLL
public DllMain ;déclare la routine publique pour exportation (voir fichier DEF)
_hModuleInstance$=8 ;instance de la DLL
_dwReason$=12 ;raison de l'appel
_pReserved$=16 ;réservé
DllMain proc near
@entry:
push ebp ;initialise la pile
mov ebp,esp
@processing:
;trace $dllmain
mov eax,handle ptr _hModuleInstance$[ebp] ;charge dans le handle global l'instance de la DLL
mov handle ptr [_hDllInstance],eax
xor eax,eax
cmp dword ptr _dwReason$[ebp],DLL_PROCESS_ATTACH ;vérifie la raison de l'appel
sete al ;réagit en conséquence
@exit:
pop ebp ;restore EBP
retn 12 ;enlève les arguments passés à la fonction
DllMain endp
;;routine d'enregistrement de la classe COM
public DllRegisterServer ;déclare la routine publique pour exportation (voir fichier DEF)
DllRegisterServer proc near
_hKey?ProgID?$=-4 ;handle destiné à contenir la clé HKEY_CLASSES_ROOT\%ProgID%
_hKeyCLSID$=-4 ;handle destiné à contenir la clé HKEY_CLASSES_ROOT\CLSID
_hKeyCLSID?GUID?Inproc$=-4 ;handle destiné à contenir la clé HKEY_CLASSES_ROOT\CLSID\%GUID%\InprocServer32
_hKeyCLSID?GUID?ProgID$=-4 ;handle destiné à contenir la clé HKEY_CLASSES_ROOT\CLSID\%GUID%\ProgID
_hKey?ProgID?CLSID$=-8 ;handle destiné à contenir la clé HKEY_CLASSES_ROOT\%ProgID%\CLSID
_hKeyCLSID?GUID?$=-8 ;handle destiné à contenir la clé HKEY_CLASSES_ROOT\CLSID\%GUID%
_pTypeLib$=-12 ;pointeur sur une interface ITypeLib
_szDllPath$=-(MAX_PATH+12) ;chaîne de caractères ASCII destinée à contenir le chemin complet de la DLL
_wcDllPath$=-(MAX_PATH*2+12) ;chaîne de caractères Unicode destinée à contenir le chemin complet de la DLL
@entry:
push ebp ;initialise la pile
mov ebp,esp
add esp,-(MAX_PATH*2+12) ;réserve l'espace sur la pile pour les variables locales
@processing:
lea eax,handle ptr _hKey?ProgID?$[ebp] ;crée/ouvre la clé HKEY_CLASSES_ROOT\%ProgID%
push eax
push offset $ProgID_MyComponent
push HKEY_CLASSES_ROOT
call RegCreateKey
test eax,eax
jnz @exit
push 22 ;assigne à la clé HKEY_CLASSES_ROOT\%ProgID% la description du composant
push offset $Desc
push REG_SZ
push NULL
push handle ptr _hKey?ProgID?$[ebp]
call RegSetValue
test eax,eax
jnz @exit
lea eax,handle ptr _hKey?ProgID?CLSID$[ebp] ;crée/ouvre la clé HKEY_CLASSES_ROOT\%ProgID%\CLSID
push eax
push offset $CLSID
push handle ptr _hKey?ProgID?$[ebp]
call RegCreateKey
test eax,eax
jnz @exit
push 38 ;assigne à la clé HKEY_CLASSES_ROOT\%ProgID%\CLSID le CLSID de notre composant
push offset $CLSID_MyComponent
push REG_SZ
push NULL
push handle ptr _hKey?ProgID?CLSID$[ebp]
call RegSetValue
test eax,eax
jnz @exit
push handle ptr _hKey?ProgID?CLSID$[ebp] ;ferme la clé HKEY_CLASSES_ROOT\%ProgID%\CLSID
call RegCloseKey ;cela nous permet également d'employer la mémoire précédemment occupée par la variable
test eax,eax ;ici, nous emploirons jusqu'à quatre fois le même emplacement mémoire sous quatre noms différents
jnz @exit
push handle ptr _hKey?ProgID?$[ebp] ;ferme la clé HKEY_CLASSES_ROOT\%ProgID%
call RegCloseKey
test eax,eax
jnz @exit
lea eax,handle ptr _hKeyCLSID$[ebp] ;crée/ouvre la clé HKEY_CLASSES_ROOT\CLSID
push eax
push offset $CLSID
push HKEY_CLASSES_ROOT
call RegCreateKey
test eax,eax
jnz @exit
lea eax,handle ptr _hKeyCLSID?GUID?$[ebp] ;crée/ouvre la clé HKEY_CLASSES_ROOT\CLSID\%GUID%
push eax
push offset $CLSID_MyComponent
push handle ptr _hKeyCLSID$[ebp]
call RegCreateKey
test eax,eax
jnz @exit
push 22 ;assigne à la clé HKEY_CLASSES_ROOT\CLSID\%GUID% la description du composant
push offset $Desc
push REG_SZ
push NULL
push handle ptr _hKeyCLSID?GUID?$[ebp]
call RegSetValue
test eax,eax
jnz @exit
push handle ptr _hKeyCLSID$[ebp] ;ferme la clé HKEY_CLASSES_ROOT\CLSID
call RegCloseKey
test eax,eax
jnz @exit
lea eax,handle ptr _hKeyCLSID?GUID?Inproc$[ebp] ;crée/ouvre la clé HKEY_CLASSES_ROOT\CLSID\%GUID%\InprocServer32
push eax
push offset $Inproc
push handle ptr _hKeyCLSID?GUID?$[ebp]
call RegCreateKey
test eax,eax
jnz @exit
push MAX_PATH ;récupère le chemin complet de la DLL à partir de son instance
lea eax,char ptr _szDllPath$[ebp]
push eax
push handle ptr [_hDllInstance]
call GetModuleFileName
test eax,eax
setz al
jz @exit
push MAX_PATH ;assigne à la clé HKEY_CLASSES_ROOT\CLSID\%GUID%\InprocServer32 le chemin complet du composant (de la DLL)
lea eax,char ptr _szDllPath$[ebp]
push eax
push REG_SZ
push NULL
push handle ptr _hKeyCLSID?GUID?Inproc$[ebp]
call RegSetValue
test eax,eax
jnz @exit
push 4 ;ajoute à la clé HKEY_CLASSES_ROOT\CLSID\%GUID%\InprocServer32 la chaîne ThreadingModel ayant pour valeur "Both"
push offset $ThreadingType
push REG_SZ
push 0
push offset $ThreadingModel
push handle ptr _hKeyCLSID?GUID?Inproc$[ebp]
call RegSetValueEx
test eax,eax
jnz @exit
push handle ptr _hKeyCLSID?GUID?Inproc$[ebp] ;ferme la clé HKEY_CLASSES_ROOT\CLSID\%GUID%\InprocServer32
call RegCloseKey
test eax,eax
jnz @exit
lea eax,handle ptr _hKeyCLSID?GUID?ProgID$[ebp] ;crée/ouvre la clé HKEY_CLASSES_ROOT\CLSID\%GUID%\ProgID
push eax
push offset $ProgID
push handle ptr _hKeyCLSID?GUID?$[ebp]
call RegCreateKey
test eax,eax
jnz @exit
push 9 ;assigne à la clé HKEY_CLASSES_ROOT\CLSID\%GUID%\ProgID le ProgID (qui est en fait le nom) de notre composant
push offset $ProgID_MyComponent
push REG_SZ
push NULL
push handle ptr _hKeyCLSID?GUID?ProgID$[ebp]
call RegSetValue
test eax,eax
jnz @exit
push handle ptr _hKeyCLSID?GUID?ProgID$[ebp] ;ferme la clé HKEY_CLASSES_ROOT\CLSID\%GUID%\ProgID
call RegCloseKey
test eax,eax
jnz @exit
push handle ptr _hKeyCLSID?GUID?$[ebp] ;ferme la clé HKEY_CLASSES_ROOT\CLSID\%GUID%
call RegCloseKey
test eax,eax
jnz @exit
push MAX_PATH ;convertit la chaîne ASCII contenant le chemin complet de notre DLL en chaîne Unicode
lea eax,wchar ptr _wcDllPath$[ebp]
push eax
push -1
lea eax,char ptr _szDllPath$[ebp]
push eax
push 0
push CP_ACP
call MultiByteToWideChar
test eax,eax
setz al
jz @exit
lea eax,pointer ptr _pTypeLib$[ebp] ;charge la Type Library
push eax
lea eax,wchar ptr _wcDllPath$[ebp]
push eax
call LoadTypeLib
test eax,eax
jnz @exit
push NULL ;enregistre la Type Library
lea eax,wchar ptr _wcDllPath$[ebp]
push eax
push pointer ptr _pTypeLib$[ebp]
call RegisterTypeLib
test eax,eax
jnz @exit
mov eax,pointer ptr _pTypeLib$[ebp] ;libère le pointeur d'interface ITypeLib
push eax
mov ecx,pointer ptr [eax]
call near ptr [ecx+2*4]
xor eax,eax ;retourne S_OK
@exit:
mov esp,ebp ;restore EBP
pop ebp
retn
DllRegisterServer endp
;;routine de vérification avant-libération de la DLL (traduction très mauvaise si quelqu'un propose autre chose...)
public DllCanUnloadNow
DllCanUnloadNow proc near
@entry:
@processing:
;trace $dllcanunloadnow
xor eax,eax
cmp dword ptr [_dwGlobalCount],eax ;teste si le compteur global est à zero
setne al ;réagit en conséquence
@exit:
retn
DllCanUnloadNow endp
;;routine de récupération de la classe fabrique (IClassFactory)
public DllGetClassObject
_pCLSID$=8 ;classe demandée
_pIID$=12 ;interface demandée
_ppObject$=16 ;pointeur sur pointeur de l'interface, destiné à recevoir l'objet
DllGetClassObject proc near
@entry:
push ebp ;initialise la pile
mov ebp,esp
@test:
;trace $dllgetclassobject
push offset CLSID_MyComponent ;teste la validité de la classe (CLSID) demandé
push pointer ptr _pCLSID$[ebp]
call IsEqualGUID
test eax,eax
jnz @processing ;réagit en conséquence
mov eax,pointer ptr _ppObject$[ebp] ;remplit le pointeur avec une valeur nulle
mov pointer ptr [eax],NULL
mov eax,CLASS_E_CLASSNOTAVAILABLE ;retourne l'erreur E_CLASSNOTAVAILABLE (classe non-supportée)
jmp @exit
@processing:
push pointer ptr _ppObject$[ebp] ;demande une interface pour la classe MyComponent
push pointer ptr _pIID$[ebp]
push offset _ClassFactoryObject
call QueryInterface@ClassFactory$IUnknown
@exit:
pop ebp ;restore EBP
retn 12 ;enlève les arguments passés à la fonction de la pile
DllGetClassObject endp
;;fonction de demande d'interface pour l'objet ClassFactory
_pIID$=12 ;interface demandée
_ppObject$=16 ;pointeur sur pointeur de l'interface, destiné à recevoir l'objet
QueryInterface@ClassFactory$IUnknown proc near
@entry:
push ebp ;initialise la pile
mov ebp,esp
@test:
;trace $queryfactory
push offset IID_IUnknown ;teste la validité de l'ineterface demandée
push pointer ptr _pIID$[ebp]
call IsEqualGUID
test eax,eax
jnz @processing
push offset IID_IClassFactory
push pointer ptr _pIID$[ebp]
call IsEqualGUID
test eax,eax
jnz @processing ;réagit en conséquence
mov eax,pointer ptr _ppObject$[ebp] ;remplit le pointeur avec une valeur nulle
mov pointer ptr [eax],NULL
mov eax,E_NOINTERFACE ;retourne l'erreur E_NOINTERFACE (interface indisponible)
jmp @exit
@processing:
inc dword ptr [_ClassFactoryObject+4] ;incrémente le compteur de l'objet ClassFactory (nous venons de créer une nouvelle inetrface)
inc dword ptr [_dwGlobalCount] ;incrémente le compteur global
mov eax,pointer ptr _ppObject$[ebp] ;remplit le pointeur avec l'adresse de l'objet ClassFactory
mov pointer ptr [eax],offset _ClassFactoryObject
xor eax,eax ;retournbe S_OK
@exit:
pop ebp ;restore EBP
retn 12 ;enlève les arguments passés à la fonction de la pile
QueryInterface@ClassFactory$IUnknown endp
;;fonction d'incrémentation du compteur de l'objet ClassFactory
AddRef@ClassFactory$IUnknown proc near
@entry:
@processing:
;trace $addfactory
inc dword ptr [_ClassFactoryObject+4] ;incrémente le compteur de l'objet ClassFactory
inc dword ptr [_dwGlobalCount] ;incrémente le compteur global
@exit:
mov eax,dword ptr [_ClassFactoryObject+4] ;retourne la valeur du compteur
retn 4 ;enlève les arguments passés à la fonction de la pile
AddRef@ClassFactory$IUnknown endp
;;fonction de libération/nettoyage de l'objet ClassFactory (cette fonction joue le rôle de destructeur quand la classe n'est pas unique/statique; ici, nous nou contenterons de décrémenter le compteur)
Release@ClassFactory$IUnknown proc
@entry:
@processing:
;trace $releasefactory
dec dword ptr [_ClassFactoryObject+4] ;incrémente le compteur de l'objet ClassFactory
dec dword ptr [_dwGlobalCount] ;incrémente le compteur global
@exit:
mov eax,dword ptr [_ClassFactoryObject+4] ;retourne la valeur du compteur
retn 4 ;enlève les arguments passés à la fonction de la pile
Release@ClassFactory$IUnknown endp
;;fonction de création d'instances
_pUnkwn$=12 ;pointeur IUnknown (pour les aggregates)
_pIID$=16 ;interface demandée
_ppObject$=20 ;pointeur sur pointeur de l'interface, destiné à recevoir l'objet
CreateInstance@ClassFactory$IClassFactory proc near
@entry:
push ebp ;initialise la pile
mov ebp,esp
@test:
;trace $createinstance
cmp pointer ptr _pUnkwn$[ebp],NULL ;teste la présence du pointeur sur IUnknown (et du même coup la demande d'aggregate)
je @processing ;réagit en conséquence
mov eax,pointer ptr _ppObject$[ebp] ;remplit le pointeur avec une valeur nulle
mov pointer ptr [eax],NULL
mov eax,CLASS_E_NOAGGREGATION ;retourne l'erreur CLASS_E_NOAGGREGATION (aggregations non-supportées)
jmp @exit
@processing:
push pointer ptr _ppObject$[ebp] ;demande une interface pour l'objet MyComponent
push pointer ptr _pIID$[ebp]
push offset _MyComponentObject
call QueryInterface@MyComponent$IUnknown
@exit:
pop ebp ;restore EBP
retn 16 ;enlève les arguments passés à la fonction de la pile
CreateInstance@ClassFactory$IClassFactory endp
;;fonction de vérouillage du serveur (de l'objet ClassFactory; étant donné que notre objet est static, nous nous contenterons de simuler l'action de cette fonction)
_dwAction$=12
LockServer@ClassFactory$IClassFactory proc near
@entry:
@test:
;trace $lockserver
cmp dword ptr _dwAction$[ebp],FALSE ;teste l'action souhaitée
je @processing$unlock ;réagit en conséquence
@processing$lock:
inc dword ptr [_ClassFactoryObject+4] ;incrémente le compteur de l'objet ClassFactory
inc dword ptr [_dwGlobalCount] ;incrémente le compteur global
@processing$unlock:
dec dword ptr [_ClassFactoryObject+4] ;décrémente le compteur de l'objet ClassFactory
dec dword ptr [_dwGlobalCount] ;décrémente le compteur global
@exit:
xor eax,eax ;retourne S_OK
ret 8 ;enlève les arguments passés à la fonction de la pile
LockServer@ClassFactory$IClassFactory endp
;;fonction de demande d'interface pour l'objet MyComponent
_pIID$=12 ;interface demandée
_ppObject$=16 ;pointeur sur pointeur de l'interface, destiné à recevoir l'objet
QueryInterface@MyComponent$IUnknown proc near
@entry:
push ebp ;initialise la pile
mov ebp,esp
@test:
;trace $queryinterface
push offset IID_IUnknown ;teste la validité de l'interface demandée
push pointer ptr _pIID$[ebp]
call IsEqualGUID
test eax,eax
jnz @processing
push offset IID_IMyComponent
push pointer ptr _pIID$[ebp]
call IsEqualGUID
test eax,eax
jnz @processing ;réagit en conséquence
mov eax,pointer ptr _ppObject$[ebp] ;remplit le pointeur avec une valeur nulle
mov pointer ptr [eax],NULL
mov eax,E_NOINTERFACE ;retourne E_NOINTERFACE (interface non-supportée)
jmp @exit
@processing:
inc dword ptr [_MyComponentObject+4] ;incrémente le compteur de l'objet MyComponent
inc dword ptr [_dwGlobalCount] ;incrémente le compteur global
mov eax,pointer ptr _ppObject$[ebp] ;remplit le pointeur avec l'adresse de l'objet MyComponent
mov pointer ptr [eax],offset _MyComponentObject
xor eax,eax ;retourne S_OK
@exit:
pop ebp ;restore EBP
retn 12 ;enlève les arguments passés à la fonction de la pile
QueryInterface@MyComponent$IUnknown endp
;;fonction d'incrémentation du compteur de l'objet MyComponent
AddRef@MyComponent$IUnknown proc near
@entry:
@processing:
;trace $addref
inc dword ptr [_MyComponentObject+4] ;incrémente le compteur de l'objet MyComponent
inc dword ptr [_dwGlobalCount] ;incrémente le compteur global
@exit:
mov eax,dword ptr [_MyComponentObject+4] ;retourne la valeur du compteur
retn 4 ;enlève les arguments passés à la fonction de la pile
AddRef@MyComponent$IUnknown endp
;;fonction de libération/nettoyage d'interface de l'objet MyComponent
Release@MyComponent$IUnknown proc
@entry:
@processing:
;trace $release
dec dword ptr [_MyComponentObject+4] ;décrémente le compteur de l'objet MyComponent
dec dword ptr [_dwGlobalCount] ;décrémente le compteur global
@exit:
mov eax,dword ptr [_MyComponentObject+4] ;retourne la valeur du compteur
retn 4 ;enlève les arguments passés à la fonction de la pile
Release@MyComponent$IUnknown endp
_TEXT ends
end DllMain
Conclusion :
J'avais oublié de dire, laissez moi un commentaire que je sache ce qui va pas ;) Dans mon style de prog aussi (mais me dites pas d'utiliser la syntaxe simplifiée pour faire plus claire ^^).
Vous n'êtes pas encore membre ?
inscrivez-vous, c'est gratuit et ça prend moins d'une minute !
Les membres obtiennent plus de réponses que les utilisateurs anonymes.
Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.
Le fait d'être membre vous permet d'avoir des options supplémentaires.