PureBasic COM Object Framework

Contents


Introduction

Avec l'introduction d'interfaces dans PureBasic, accéder aux objets externes COM (Component Object Model) est devenu assez simple (en termes de quantité de code nécessaires à utiliser). Cependant, créer des objets COM dans PB pour être utilisé à l'extérieur, ou avec des commandes API est encore quelque chose qui requiert beaucoup de connaissances au sujet de PB et de COM et un certain travail à faire. Ce framework essaie de changer ca. Cela fournit une série de macros pour facilement définir un objet et aussi automatiser la plupart des tâches communes pour l'implémentation de tous les objets (comme l'implémentation de l'interface IUnknown). En outre, cela fournir une fonctionnalité de débogage supplémentaire pour tracer les bogues dans votre implémentation. Il y a quelques macros qui sont utiles pour le développement général COM. Haut

Caractéristiques

Général - Aucun préprocesseur utile. Tout est uniquement en PB. - Tout est contenu dans les fichiers residents et includes, minimisant les problèmes possibles avec les versions futures de PB - Fonctionnel en mode Threadsafe et Unicode - Fonctionne avec l'option du compilateur EnableExplicit Implémentation de l'objet - Macros simples pour définir la structure de la classe - Interfaces multiples supportées dans une classe (supérieur à 20) - Implementation d'IUnknown est complétement faite par le framework - Support du Constructeur/Destructeur - Pour les méthodes non implémentés, une méthode par défaut est automatiquement insérée qui retourne #E_NOTIMPL - Une VTable est créé avec un simple appel de macro - Macros pour une définition facile et manipulation de valeurs de type GUID (IID, CLSID, ...) Débogage - Suivi complet de tous les appels pour les méthodes des objets, incluant l'affichage des valeurs de retour - Suivi des appels pour les objets morts (déjà libérés) - Capture des appels de méthode en dehors de la VTable (pour trouver des appels de mauvaises interfaces) - La totalité de la sortie du debug pour le suivi peut être customisé avec 'DebugLevel' - Conversion des valeurs GUID et HRESULT en texte pour un débogage plus simple - Avec le débogueur inactif, la sortie du debug est sortie via OutputDebugString_() pour un débogage plus facile des dlls Haut

Licence

Copyright (c) 2006 Timo Harter L'autorisation est accordée, à titre gratuit, à toute personne obtenant une copie de ce logiciel et des fichiers de documentation (le "Logiciel"), pour faire face le Logiciel sans restriction, y compris, sans s'y limiter, les droits d'utiliser, copier, modifier, fusionner, publier, distribuer, concéder sous licence, et / ou vendre des copies du Logiciel, et à autoriser les personnes auxquelles le Logiciel est fourni de faire de ainsi, sous réserve des conditions suivantes : L'avis de copyright ci-dessus et la présente autorisation doivent être inclus dans toutes les copies ou parties substantielles du Logiciel. LE LOGICIEL EST FOURNI "EN L'ETAT", SANS GARANTIE DE QUELQUE NATURE QUE CE SOIT, EXPLICITE OU IMPLICITE, Y COMPRIS, MAIS SANS S'Y LIMITER LES GARANTIES DE QUALITE MARCHANDE, D'ADEQUATION A UN USAGE PARTICULIER ET D'ABSENCE DE CONTREFAÇON. EN AUCUN CAS LES AUTEURS OU DROIT D'AUTEUR TITULAIRES ETRE TENUE RESPONSABLE DE TOUT DOMMAGE, RéCLAMATION OU AUTRE RESPONSABILITé, QUE CE SOIT DANS UNE ACTION DE CONTRAT, UN DéLIT OU AUTRE, DéCOULANT DE OU EN RELATION AVEC LA LOGICIEL OU LES RAPPORTS D'UTILISATION OU D'UNE AUTRE DANS LE LOGICIEL. Haut

Installation

Pour installer le framework, décompressez le fichier zip directement dans votre dossier PureBasic. Le dossier 'ComFramework' doit être placé dans le dossier principal de PureBasic et le contenu du dossier 'Residents' doit être copié dans le dossier des résidents de PureBasic. L'emplacement et le nom du dossier 'ComFramework' est important, car c'est de là que les fichiers d'include seront automatiquement inclus si les macros du framework sont utilisés. Fichiers inclus dans le package : \Residents\ - Fichiers Resident contenant les macros de base du framework \ComFramework\ - Fichiers d'includes. A inclure dans la source si le framework est utilisé \ComFramework\Interfaces\ - Information sur chaque interface supporté. A inclure dans la source au besoin \ComFramework\Exemples\ - Sources d'exemple Haut

Informations générales

Le framework COM utilise des noms commencant avec '__COM' pour toute utilisation interne, donc pour éviter les conflits, vous ne devrez pas utiliser de noms dans votre code qui commencent par '__COM'. NOTE: Le but de ce framework est d'implémenter les interfaces qui sont définies dans le Microsoft Component Object Model. L'intention n'est pas de fournir des fonctionnalités générales OOP pour PureBasic. Pour automatiser la plupart du travail, le framework a besoin d'informations sur les interfaces données, c'est pourquoi seules les interfaces prédéfinies peuvent être implémentées, et aucune personnalisées (actuellement 855 interfaces sont connues dans le framework). Le framework est implémenté comme des macros dans des fichiers résidents et un série de fichiers à inclure. Vous n'avez pas besoin d'inclure de fichiers manuellement. Ils seront inclus automatiquement avec les macros des fichiers résidents, dés que nécessaire. Haut

Macros de gestion des GUID

Les macros de GUID sont indépendants du reste du framework (les utiliser ne causera pas l'inclusion des fichiers include), donc elles peuvent aussi être utilisées uniquement pour faciliter l'accès à des objets externes COM. Les macros sont définies sous 3 noms, finissant par ...GUID, ...IID et...CLSID. Ce sont exactement les mêmes, elles existent juste séparément pour permettre d'écrire du code lisible. DefineGUID(Name, long, word1, word2, byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8) DefineIID(Name, long, word1, word2, byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8) DefineCLSID(Name, long, word1, word2, byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8) Ce sont les mêmes que les macros correspondantes en C. A utiliser pour définir une GUID dans une ligne. La macro crée actuellement une DataSection avec un label appelé 'Name' et les données suivantes, donc le GUID peut être accédé plus tard via '?Name', où Name est le nom utilisé dans la définition. La macro est sûre pour un usage multiple. De multiples définitions de la même GUID seront ignorées, ce qui permet de simplement définir une GUID où c'est utile sans s'inquiéter de ce qui a été défini dans une autre partie du projet (d'autres includefile par exemple). Exemple:
; Définit l'IID IUnknown ; DefineIID(IID_IUnknown, $00000000, $0000, $0000, $C0, $00, $00, $00, $00, $00, $00, $46) ; ; Utilise la valeur IID ; SomeObject\QueryInterface(?IID_IUnknown, @NewPointer.IUnknown)
DefineKnownGUID(Name) DefineKnownIID(Name) DefineKnownCLSID(Name) Le framework inclue un fichier resident appelé 'GuidList.res', qui contient une énorme macro contenant une liste des valeurs GUID extraites du Microsoft Platform SDK aussi bien que du DirectX9 SDK. (Nombre total : 2705 GUIDs) Ces macros permettent de facilement définir une valeur connue par son nom. La plupart des valeurs principales relatif à COM sont connues. Comme la liste est contenue dans une seul macro, la rapidité générale de compilation n'est pas affectée par la liste énorme. Ces macros sont protégés contre des utilisations multiples de la même manière que les macros Define...(), donc définir deux fois une valeur avec une de ces macros n'aura pas d'effet. Exemple:
; Définit l'IDD IUnknown, de la même manière ; Le résultat est le même que l'exemple ci dessus ; DefineKnownIID(IID_IUnknown)
CompareGUID(guid1, guid2) CompareIID(iid1, iid2) CompareCLSID(clsid1, clsid2) Compare les deux valeurs GUID et retourne 1 si elles sont égales et 0 autrement. C'est juste un simple wrapper de CompareMemory() afin d'écrire un code plus lisible. Exemple:
; Retourne 1 bien sûr ; DefineKnownIID(IID_IUnknown) Debug CompareIID(?IID_IUnknown, ?IID_IUnknown)
Haut

Implémentation de l'objet

Les étapes suivantes sont nécessaires pour implémenter un objet COM en utilisant le framework

Définir la classe

La suite est le squelette d'une définition pour une classe d'objet COM. L'ordre est important. Ce bloc complet peut ne pas être mis à l'intérieur d'une procédure.
COMClass(<ObjectName>) ; définit les interfaces implémentées. Au moins une telle déclaration est nécessaire COMInterface(<ObjectName>, <InterfaceName> [, <ExtendedInterface> [, <ExtendedInterface> ...]]) ; définit les variables locales de la classe (facultatif) COMClassData(<ObjectName>) Value1.l Value2$ EndComClassData ; définit le constructeur (facultatif) COMConstructor(<ObjectName> [, (<Argumentlist>)]) ; retourne une interface différente du constructeur de IUnknown (facultatif) COMConstructorReturn <InterfaceName> EndCOMConstructor ; définit le destructeur (facultatif) COMDestructor(<ObjectName>) EndCOMDestructor EndCOMClass(<ObjectName>, <Interface1>, <Interface2>, ...)
Détais : COMClass(<ObjectName>) Commence la définition d'une nouvelle classe dans le framework COM. Une structure avec le nom sera créée pour représenter la structure des objets et les données privées. COMInterface(<ObjectName>, <InterfaceName> [, <ExtendedInterface> [, <ExtendedInterface> ...]]) Définit quelles interfaces seront implémentées dans cette classe. Si une interface qui sera implémenté s'étend à une autre, le framework ne saura pas que l'interface de base étendu est supporté aussi bien. Vous pouvez la faire reconnaître ces interfaces, mais aussi en spécifiant les interfaces qui sont contenues au sein de l'interface implémentée aussi bien que les paramètres facultatifs (jusqu'à 5 paramètres facultatifs sont possibles). NOTE: IUnknown est toujours automatiquement implémentée par le framework, donc ne le spécifiez pas ! Une classe peut supporter seulement 20 interfaces (en incluant les étendues). Pour toute interface qui est implémentée ici (ou spécifiée comme une interface étendue), la valeur de l'IID correspondante sera automatiquement défini avec la macro DefineIID(), comme cela est nécessaire par le framework. DefineIID() est protégé contre l'utilisation multiple, donc une double éfinition n'est pas un problème. COMClassData(<ObjectName>) / EndComClassData C'est facultatif et doit être défini après toutes les définitions de COMInterface(). Cela permet d'ajouter des données privées à la structure qui est définie par la macro COMClass(). étant donné que les valeurs à l'intérieur seront membres de la structure, toutes les valeurs doivent inclure le type. Donc les tableaux statiques comme les structures sont possibles. COMConstructor(<ObjectName> [, (<Argumentlist>)]) / EndCOMConstructor C'est facultatif et doit être mis aprés COMClassData(). Cela permet de définir un constructeur personnalisé pour la classe. Cela permet de définir une liste facultative d'argument pour la procédure du constructeur (cela doit être le second argument dans l'appel et DOIT inclure les parenthèses). Pour créer un nouvel objet plus tard dans le cas, 'New_ObjectName()' est utilisé. Si le constructeur spécifie une liste d'arguments, ces arguments seront utilisés dans la procédure New comme cela : 'New_ObjectName()' A l'intérieur du constructeur, un pointeur de structure local appelé *THIS. est disponible pour accéder aux données privées du nouvel objet. COMConstructorReturn <InterfaceName> Par défaut, la fonction New_ObjectName() retournera un pointeur vers l'interface IUnknown d'un nouvel objet. Si un pointeur d'une autre interface peut être retourné ici, cela peut être fait en appelant COMConstructorReturn à l'intérieur du constructeur. Cela quitte le constructeur, tout comme ProcedureReturn. COMDestructor(<ObjectName>) / EndCOMDestructor Permet de définir un destructeur personnalisé qui est appelé une fois toutes les références de l'objet libérées, et avant que l'objet soit libéré. Comme le constructeur, vous pouvez utiliser le pointeur *THIS. pour accéder aux données privées. EndCOMClass(<ObjectName>, <Interface1>, <Interface2>, ...) C'est actuellement une grosse macro qui fera tout le travail d'implémentation principale pour la classe. Cela définira les méthodes de IUnknown qui gèrent la durée de vie des objets. Cela définira aussi la procédure New_() qui est utilisé pour créer une nouvelle instance de la classe. Pour les raisons de l'implémentation, vous avez besoin de spécifier encore toutes les interfaces qui sont implémentées dans la classe dans l'appel de la macro. Cela inclue les interfaces qui sont spécifiées comme des interfaces étendues dans les appels de COMInterface(). Exemple:
; C'est un exemple basique qui définit une classe qui implémente seulement l'interface IDispatch ; COMClass(MyDispatch) COMInterface(MyDispatch, IDispatch) ; étends seulement IUnknow, qui est géré automatiquement COMClassData(MyDispatch) ; définit nos entrées dans la structure de l'objet Value1.l Value2.l EndCOMClassData COMConstructor(MyDispatch, (Value1, Value2)) ; définit un constructeur qui prend les deux valeurs comme arguments *THIS\Value1 = Value1 *THIS\Value2 = Value2 COMConstructorReturn IDispatch; Nous voulons que le pointeur IDispatch soit retourné ; NOTE: IDispatch s'étend vers IUnknown, donc on pourrait penser que l'implémentation des pointeurs de IUnknown ; et de IDispatch serait identique. Ce n'est pas le cas. Le framework implémente une interface IUnknown séparée ; qui n'est pas étendue par n'importe quoi. Ce pointeur est initialement retourné si COMConstructorReturn n'est ; pas utilisé. Il est également renvoyé à tout moment que IUnknown est demandé pour l'objet, puisque c'est un ; principe basique de COM qu'une requête pour IUnknown sur un objet doit toujours retourner le même pointeur. EndCOMConstructor COMDestructor(MyDispatch) ; fait n'importe quelle action de libération ici. ; *THIS\Value1 et *THIS\Value2 peuvent être aussi être accédé ici. EndCOMConstructor EndCOMClass(MyDispatch, IDispatch) ; après que toutes les autres opérations soient faites, after all other stuff is done, un tel objet est créé avec Dispatch.IDispatch = New_MyDispatch(1, 2) ; où 1,2 sont les deux arguments pour le constructeur ; NOTE: Cette partie seule est inutile. Pour créer des objets fonctionnels, vous avez besoin d'implémenter les ; méthodes et appeler les macros de création de VTable nécessaires.
Haut

Implémenter les méthodes

Les méthodes sont de simples procédures, qui doivent être nommées d'aprés un schéma de nommage spécifique, et avoir un pointeur appellé *THIS. comme premier argument (une méthode avec 0 arguments aura seulement le pointeur *THIS dans la définition). De plus, la macro 'COMMethodOf(ObjectName)' doit être appelé tout d'abord à l'intérieur de la procédure. Pour du code lisible, la macro peut être directement sur la ligne de la procédure, même sans aucun séparateur. Une procédure méthode ressemble à cela : Procedure <ObjectName>_<InterfaceName>_<MethodName>(*THIS.<ObjectName>, <Argument list>) COMMethodOf(<ObjectName>) EndProcedure Exemple:
Procedure MyDispatch_IDispatch_Invoke(*THIS.MyDispatch, ...) COMMethodOf(MyDispatch) EndProcedure
C'est important que le nom ressemble exactement à ca, pour que la macro de création de VTable puisse détecter quelles méthodes sont implémentées ou pas. C'est important de spécifier le type. pour le pointeur *THIS, autrement la macro COMMethodOf() ne fonctionnera pas correctement. Le but de cette macro est d'ajuster le pointeur *THIS. Si la macro n'est pas utilisé, *THIS pointera vers la base de l'Interface appelé, pas vers la base de la structure de l'objet. Il y a deux pointeurs diffférents, donc accéder aux valeurs locales à l'intérieur de la structure de l'Objet (défini avec COMClassData() ci-dessus) échouera. La macro COMMethodOf() assure que le pointeur est défini à la structure de base, donc les valeurs peuvent être atteintes avec cela. Donc théoriquement, cette macro peut être laissé de côté si rien n'est atteint avec le pointeur *THIS à l'intérieur de la procédure, mais il est recommandé de l'ajouter partout pour la consistance, et ainsi éviter d'introduire une bogue difficile à trouver si on le laisse là où c'est vraiment nécessaire. Notes: Ne jamais implémenter les méthodes IUnknow QueryInterface(), AddRef(), Release() pour n'importe quel Interface. C'est toujours mis en place par le framework. Si une méthode de l'interface n'est pas défini dans la source, la macro de création de la VTable le détectera et insérera automatiquement une méthode par défaut qui retourne #E_NOTIMPL. Donc la VTable ne contiendra jamais aucun pointeur NULL qui pourrait diriger vers un crash. #E_NOTIMPL est accepté comme un résultat de plusieurs parties facultatives des Interfaces COM, donc vous pouvez laisser ces méthodes que vous n'avez pas besoin d'implémenter pour l'interface, et la méthode par défaut sera insérée. Si une interface s'étend à une autre, les méthodes doivent seulement être implémentées pour la grosse qui s'étend aux autres. Ces méthodes seront réutilisées pour l'interface étendue. Si une méthode devait seulement retourner une certaine valeur mais rien d'autre, la macro COMEmptyMethod() peut être utilisée pour éviter le besoin de déclarer la procédure méthode complètement : COMEmptyMethod(<ObjectName>, <InterfaceName>, <MethodName>, <ReturnValue>) doit être une constante de type long. La macro implémentera une méthode avec le bon nombre d'arguments pour cette méthode qui ne fera rien mais retournera la valeur de retour donnée. Si les interfaces dans la classe on la même méthode (par exemple si les deux s'étendent vers la même interface de base), la macro COMReuseMethod() peut être utilisée pour dire au framework d'utiliser la méthode qui est implémentée pour une interface pour une autre aussi bien, donc le code n'a pas besoin d'être dupliqué. COMReuseMethod(<ObjectName>, <ImplementingInterface>, <MethodName>, <ReusingInterface>) est l'interface qui a implémentée la procédure méthode actuelle. est celle qui l'on utilisera aussi. Une méthode peut être réutilisée par beaucoup d'interfaces, mais seulement par la même classe.
Haut

Appeler les macro pour créer les VTables

En raison des limitations des macros PB (bien, elles sont assez puissantes comme le prouve ce framework, mais ici j'ai rencontré un mur), les VTables pour la classe ne peuvent pas être créées automatiquement. Vous avez besoin d'appeler une macro par Interface dans la classe pour les construire. Espérons qu'il y aura une manière plus confortable de le faire dans le futur, mais actuellement, c'est mieux que de créer les VTables manuellement. Les macros ont cette forme : BuildCOMVTable_<InterfaceName>(<ObjectName>) Le nom de l'Interface est une partie du nom de la macro La macro requise pour chaque interface est automatiquement incluse avec la source une fois que c'est inclus dans la classe avec la macro COMInterface(). Notes importantes : Ces macros doivent être mises dans le code en dessous de TOUTES les implémentations de méthodes et autres macros relatives aux méthodes. C'est parce que les macros vérifieront quelles méthodes seront actuellement implémentées pour sélectionner les méthodes adéquates par défaut ou les méthodes réutilisées dans la VTable. Ces macros produisent le code qui doit être exécuté, contrairement les macros de définition de la classe qui produisent seulement les déclarations et les procédures. Les macros doivent être exécutées AVANT que n'importe quelle instance de la classe soit créé. Ils peuvent être placés à l'intérieur des procédures. Pour une application, cela peut être quelque part avant que le code réel de l'application ne commence. Pour une DLL COM, cela peut être la procédure AttachProcess(). Vous pouvez aussi placer ces macros à l'intérieur d'une procédure et l'appeler à partir du constructeur de la classe. Cela fonctionnera aussi, comme ensuite les VTables sont remplies avant la première interface de l'objet qui est en fait appelé. Vous ne pouvez pas les placer à l'intérieur du constructeur directement, comme elles ont besoin d'être en dessous de toutes les implémentations de méthode. Exemple :
BuildCOMVTable_IDispatch(MyDispatch) ; construit la VTable pour l'interface IDispatch de la classe MyDispatch
Haut

Créer des instances d'objets

Une fois que toutes les étapes ci-dessus sont réalisées (définition de la Classe, implémentation des méthodes, création de la VTable), l'implémentation est complète et les objets peuvent être créés. C'est fait au travers d'une procédure appelée 'New_()'. Cela retourne le pointeur de l'objet IUnknown à moins que COMConstructorReturn soit utilisé dans le constructeur pour retourner un pointeur différent de l'interface. Si le constructeur était défini avec des paramètres supplémentaires, ceux-ci doivent être transmis aussi à la procédure New_(). Les nouveaux objets sont créés avec un nombre de références à 1. Un objet est détruit quand son nombre de références atteint 0. Cela signifie qu'une fois que l'utilisation de l'objet est complétée, vous devez libérer votre propre référence de l'objet en appelant la méthode Release() de l'interface. Exemple:
Dispatch.IDispatch = New_MyDispatch(1, 2) ; utiliser la définition de la classe des exemples ci-dessus ; fait quelque chose avec l'objet (passage à la fonction API, etc) Dispatch\Release() ; libère notre référence de l'objet. Si aucune autre référence reste, il sera libéré.
Haut

Débogage

Pour activer la fonctionnalité du débogage du frameword, ajouter le mot-clé 'EnableCOMDebugging' au début de la source (ou avant les définitions de classe). Utiliser cette macro entraînera le framework à ajouter le code de déboggage nécessaire pour suivre les appels et autres comme décrit ci-dessous. NOTE: Quand le débogage est activé, le framework incluera de grands morceaux de données avec l'exécutable (comme les listes de toutes les valeurs d'IID et de HRESULT), donc ce n'est pas recommandé de laisser cette appel à l'intérieur d'une compilation finale.

Sortie du débogage

Si le débogueur de PureBasic est activé lors de la compilation de la source, la sortie du débogage sera affiché dans le débogueur. Si le débogueur n'est pas activé, mais que EnableCOMDebugging est défini, la sortie du débogage, la sortie du débogage sera affiché avec l'API OutputDebugString_(). Cela permet de déboguer des DLLs de type COM où le débogueur PB ne peut être utilisé. Cette sortie peut être lu avec des programmes de débogage comme DebugView (http://www.sysinternals.com/Utilities/DebugView.html) Haut

Informations fournis sur le débogage

Plusieurs niveaux d'informations sur le débogage peuvent être fournis. Ils peuvent être définis avec la commande PB 'DebugLevel'. Les niveaux les plus hauts incluent aussi la sortie de tous les niveaux ci-dessous . Les niveaux suivants sont disponibles (dans l'ordre croissant).
#COM_DEBUG_Critical #COM_DEBUG_UnknownInterface #COM_DEBUG_ObjectStats #COM_DEBUG_MethodEntry #COM_DEBUG_MethodLeave #COM_DEBUG_FormatResult #COM_DEBUG_All
#COM_DEBUG_Critical Débogue seulement les informations au sujet des conditions critiques (cas qui sont d'habitude suivis d'un crash). Une telle situation existe si un appel pour une interface en dehors de sa VTable est rencontré. Cela se passe quand l'appelant s'attendait à une interface plus importante qu'elle ne l'était réellement. Même si le suivi des objets morts est activé (cf. ci-dessous), un appel capturé pour un objet mort sera reporté comme critique. #COM_DEBUG_UnknownInterface Affiche un message quand quelque chose interroge votre objet (avec QueryInterface()) pour une interface que l'objet ne supporte pas. Le message inclue le nom de l'IID qui a été interrogé. Cette condition n'est pas nécessairement un bogue, si l'implémentation d'IUnknown retourne correctement #E_NOTIMPL, et que les fonctions API interrogent communément des interfaces multiples pour tester quel fonctionnalité est disponible. L'information de débogage peut encore être très intéressante, car cela montre quelles interfaces l'appelant attendait effectivement. #COM_DEBUG_ObjectStats Affiche l'information au sujet de la vie (nombre de références) de l'objet. Cela suit les appels de AddRef()/Release() aussi bien que QueryInterface(), et affiche le nombre courant. Cela affiche aussi un message quand l'objet est finalement libéré. #COM_DEBUG_MethodEntry Affiche tous les appels de méthode sur l'objet. Trés intéressant pour suivre quel séquence d'appel dirige vers certains crashs par exemple. #COM_DEBUG_MethodLeave Affiche les valeurs retournées pour tous les appels de méthode. #COM_DEBUG_FormatResult Affiche la constante équivalent à HRESULT qui correspond à la valeur de retour (si c'est une valeur d'erreur), aussi bien que une représentation au format texte de la valeur. #COM_DEBUG_All Le plus haut niveau, inclue tous les autres. C'est le niveau par défaut si 'DebugLevel' n'est pas utilisé. Note: DebugLevel est une déclaration au moment de la compilation, donc cela a un effet sur toutes les lignes compilées après cela, pas les lignes exécutés après ca. Changer le niveau de débogage entre l'implémentation des classes COM entraine les deux à avoir un niveau différent lors de la sortie. Donc les appels d'une classe peuvent être suivi plus strictement, tandis que pour les autres, seulement les conditions critiques sont attrapés par exemple. Puisqe DebugLevel affecte seulement la sortie du débogueur PureBasic, la totalité des sorties pour OutputDebugString_() ne peut être changé avec cela. Si le débogueur PB est désactivé, les déclarations DebugLevel sont ignorées, et le framework sortira simplement toutes les données de débogage (égal à utiliser #COM_DEBUG_All). Haut

Suivi des objets morts

Une source commune des bogues est lié aux problèmes de duré de vie des objets. Par exemple si Release() est appelé trop souvent, le dernier appel sera sur un appel qui aura été libéré (un objet mort). D'habitude de tels appels résultent en un crash, bien que quelques fois ils réussissent parce que la mémoire de l'objet n'a pas été écrasé par quelque chose d'autre, ce qui rend plus difficile de trouver de tels bogues. Ce framework fournit un suivi de tous les objets libérés pour identifier de tels appels. Tout appel sur une méthode d'un objet qui a été libéré sera reporté avec le niveau de débogage #COM_DEBUG_Critical. Pour atteindre cela, la mémoire de tous les objets précédemment existants devra être alloué jusqu'à la fin du programme. Cela peut conduire à une grande consommation de mémoire des objets qui sont créés/libérés fréquemment. C'est pourquoi cette option n'est pas activé par défaut. Pour l'activer, utilisez la déclaration 'EnableCOMDeadTracking' statement. Haut

Autres fonctions de débogage

En outre du suivi automatique des objets, le framework fournit quelques fonctions supplémentaires à des fins de débogage (seulement disponible si 'EnableCOMDebugging' est utilisé). DisplayCOMObjectList() Cette fonction affiche simplement une liste de tous les objets actuellement existants sur la sortie de débogage. La sortie contient le type de l'objet, aussi bien que le nombre de références. Cette fonction peut être utile par exemple à la fin d'un programme pour trouver les objets qui n'ont pas été correctement libérés. DisplaySupportedInterfaces(*Object) Cette fonction affiche toutes les interfaces que l'objet donné supporte (fonctionne seulement pour les interfaces qui sont connus du framework, et non pour les personnalisées). C'est très utile de connaî les caractéristiques supportées par un objet. Constant$ = GetCOMErrorConstant(ErrorCode) Retourne le nom de la constante pour une valeur de type HRESULT (valeur de retour pour les méthodes) Cela couvre seulement les valeurs négatives, si les valeurs >=0 indiquent le succès et peuvent avoir de nombreuses significations en fonction de la méthode. Message$ = GetCOMErrorMessage(ErrorCode) Retourne une représentation textuelle pour la valeur de l'erreur de type HRESULT. Ces messages étaient extraits de WinError.h. GUIDFromName(Name$) IIDFromName(Name$) CLSIDFromName(Name$) Retourne une valeur de type GUID pour le nom donné. IIDFromName("IID_IUnknown") par exemple retourne l'IID de IUnknown. Name$ = NameFromGUID(guid) Name$ = NameFromIID(iid) Name$ = NameFromCLSID(clsid) Retourne le nom pour le GUID donné. C'est trés utile quand des valeur inconnues sont rencontrés afin de savoir ce qu'elles représentent actuellement. NOTE: Contrairement aux autres macros (où ...GUID(), ...IID() et ...CLSID() font la même chose), ici chaque fonction est différente. La raison est que les valeurs IID et CLSID existent avec les mêmes noms, donc NameFromGUID() recherche seulement les noms commencant avec 'GUID_', NameFromIID() recherche seulement les valeurs 'IID_' et NameFromCLSID() recherche seulement les valeurs 'CLSID_'.
Haut