menu
  Home  ==>  articles  ==>  prog_objet_composants  ==>  dump_interface   

dump interface - John COLIBRI.


1 - Introduction

Nous allons présenter ici l'organisation mémoire des classes et des interfaces. Ce programme permet de confirmer les descriptions fournies dans les manuels Delphi. Nous examinerons:
  • la structure d'un objet
  • les VMT
  • les IMT (méthodes des interfaces) et les thunks associés
Nous supposons que le lecteur est familier avec:
  • l'utilisation des Classes contenant des procédures Virtual
  • la définition d'Interfaces et leur implémentation à l'aide de Classes
  • le surtypage d'objet par AS
Le programme complet sera capable d'afficher les différentes parties de la mémoire en expliquant les liens entre ces éléments.


2 - Architecture

2.1 - Un objet ayant des méthodes VIRTUAL

Comme nous l'avons présenté dans le livre programmation objet, l'organisation mémoire d'un programme comportant des classes ayant des méthodes virtuelles est la suivante:
  • le code contient le code des méthodes, virtuelles ou non
  • la mémoire contient une table stockant les adresses des méthodes virtuelles (la Virtual Method Table)
  • chaque objet comporte, au début de la zone des données, un pointeur vers la table des méthodes virtuelles chaque objet contient un pointeur vers la table des méthodes virtuelles de sa classe.
Dans la figure qui suit, nous supposons que:
  • notre classe comporte 3 méthodes virtuelles (figurant en vert clair dans le code présenté en violet)
  • la VMT est en jaune et contient des pointeurs vers chacune des méthodes
  • nous avons présenté deux objets (en bleu clair), chacun contenant un pointeur vers la VMT de cette classe

Si notre programme contenait une seconde classe héritant de celle-ci, il faudrait ajouter:

  • les trois méthodes virtuelles de la classe descendante (si elles sont différentes de celles de son ancêtre)
  • la VMT de cette nouvelle classe pointant vers les méthodes virtuelles
  • pour chaque descendant, les données, avec le pointeur vers la VMT de la classe descendante

2.2 - Une classe avec des Interfaces

Voyons à présent ce qui se passe lorsqu'une classe implémente des Interfaces. Nous supposons:
  • que nous avons défini quelques Interfaces
  • que ces Interfaces sont implémentées par une Classe Delphi descendant de tInterfacedObject
Dans ce cas:
  • le code comporte les méthodes de la classe (celles qui implémentent les Interfaces et les autres)
  • l'objet est structuré pour pouvoir accéder aux méthodes virtuelles ainsi qu'aux méthodes des Interfaces
Pour les méthodes virtuelles "pure" (n'implémentant pas une Interface), la mécanique est celle décrite ci-dessus.

Pour les méthodes implémentant une méthodes spécifiée dans une Interface:

  • l'objet contient pour chaque Interface un pointeur vers une table permettant d'accéder aux méthodes de l'Interface:
    • la table correspondant à tInterfacedObject, correspondant à l'Interface iUnknown
    • une table par Interface supplémentaire
  • chaque variable de type Interface pointera vers ces zones de l'objet.
Si une méthode de la classe est à la fois Virtual et implémente une méthode de l'Interface, elle doit pouvoir être appelée à la fois par une variable objet, et un variable Interface. Dans le code de cette méthode, nous pourrons accéder aux données membres de la Classe. Pour cela, Delphi passe toujours en paramètre implicite:
  • soit l'objet
  • soit la variable Interface qui a appelé la méthode
C'est le paramètres Self (dont l'usage est facultatif pour accéder aux champs ou aux méthodes de l'objet).

Or si nous appelons à partir d'un objet, le pointeur désigne le début de la zone de l'objet, mais si nous appelons à partir d'une variable Interface, ce pointeur a une valeur différente (il pointe un peu plus loin). Pour résoudre cette difficulté, Delphi effectue un ajustement de la façon suivante:

  • pour l'objet, l'adresse de la VMT est la bonne
  • pour une variable Interface, l'adresse dans l'IMT désigne un petit fragment de code qui ajuste la valeur de la variable Interface pour que cette valeur soit la même que celle de l'objet. Ce code (thunk en anglais) se contente d'ajouter la différence d'adresse, et appelle ensuite la méthode (par un JMP assembleur).
Supposons que nous ayons:
  • une Interface comportant deux méthodes
  • qui est implémentée par une Classe
    • qui descend de tInterfacedObject et qui implémente une Interface
    • qui contient
      • une méthode Virtual pure (pas liée à l'Interface)
      • les deux méthodes de l'Interface, l'une Virtual l'autre non
La mémoire comporte successivement:
  • le code des 3 procédures (en vert clair)
  • les thunk de relocations pour IUnknown, et pour les deux méthodes implémentant l'Interface (en bleu sombre)
  • les IMT pour iUnknown et pour l'Interface (en jaune)
  • la VMT (en jaune)
  • les données d'un objet, avec les pointeurs vers la VMT, vers l'IMT de iUnknown, et l'IMT de l'Interface (en bleu clair)
  • les variables globales (en rouge)

Notez que:

  • l'ordre des données n'est pas tout à fait respecté (par exemple les VMT \ IMT sont à des adresses plus faibles que le code), mais la figure a été dessinée pour correspondre à l'ordre de la présentation et pour simplifier le dessin des liens. De plus les VMT \ IMT sont actuellement dans le code 386 (et non dans les données 386)
  • les procédures correspondant aux 3 méthodes de iUnknown ne sont pas présentées et les thunks correspondants non plus (ni pour iUnknown, ni pour l'Interface). En fait:
    • l'IMT de iUnknown contient 3 entrées, et 3 thunks correspondant
    • l'IMT de l'Interface contient 5 entrées (3 pour iUnknown et 2 pour nos méthodes) et donc 5 thunks
Le dump mémoire ci dessous permettra de présenter exactement où se trouvent toutes ces données


3 - Affichage Brut

3.1 - La mise en place

Nous allons utiliser:
  • deux Interfaces
  • une Class qui les implémente
  • un objet de base
  • trois variables d'Interface obtenues à partir de l'objet par AS

3.2 - Les définitions

Voici les Interfaces:

    type i_computeInterface
                        ['{0C5C2FE0-DD74-11D4-A354-005004125613}']
                        function set_side(p_sideInteger): HResultstdcall;
                        function compute_area(var pv_areaInteger): HResultstdcall;
                      end;
         i_journalInterface
                      ['{C444E017-65B0-4AEB-B05C-6D473F1BE1EC}']
                      procedure write_to_journal;
                      procedure write_and_display;
                    end// i_journal

Voici la Class

    type ci_squareclass(TInterFacedObjecti_computei_journal)
                      m_sideInteger;
                      m_areaInteger;

                      constructor create;

                      function set_side(p_sideInteger): HResultstdcall;
                      function compute_area(var pv_areaInteger): HResultstdcall;

                      procedure write_to_journal;
                      procedure write_and_displayvirtual;

                      procedure analyze;
                      procedure analyze_virtualVirtual;

                      Destructor DestroyOverride;
                    end// ci_square

et son implémentation:

    constructor ci_square.create;
      begin
        m_side:= $1111;
        m_area:= $2222;
      end// create

    function ci_square.set_side(p_sideInteger): HResult;
      begin
        m_side:= p_side;
      end// set_side

    function ci_square.compute_area(var pv_areaInteger): HResult;
      begin
        pv_area:= m_sidem_side;
      end// compute_area

    procedure ci_square.write_to_journal;
      begin
        display('ok');
      end// write_to_journal

    procedure ci_square.write_and_display;
      begin
        display('write_and_display');
      end// write_and_display

    procedure ci_square.analyze;
      begin
        m_side:= $7777;
      end// analyze

    procedure ci_square.analyze_virtual;
      begin
        m_side:= $8888;
      end// analyze_virtual

    Destructor ci_square.Destroy;
      begin
        Inherited;
      end// Destroy

L'ensemble se trouve dans l'unité u_ci_area téléchargeable ci-dessous.

3.3 - Les adresses des méthodes

Pour afficher la position des méthodes, nous présentons au format hexadécimal l'adresse de chaque méthode obtenue par @:

    display(f_integer_to_hex(@ ci_area.set_side));

Pour les méthodes qui nous intéressent, nous obtenons ainsi:

 
          463548: 55 8B EC 83  : <= ci_square.create 
          463594: 55 8B EC 51  : <= ci_square.set_side 
          4635AC: 55 8B EC 51  : <= ci_square.compute_area 
          4635D0: 55 8B EC 51  : <= ci_square.write_to_journal 
          4635F0: 55 8B EC 51  : <= ci_square.write_and_display 
          463620: 55 8B EC 51  : <= ci_square.analyze 
          463634: 55 8B EC 51  : <= ci_square.analyze_virtual 
          463648: 55 8B EC 83  : <= ci_square.destroy 
          

3.4 - Le Dump Simple

Nous commençons par afficher les groupes de données par une primitive simple qui présente l'adresse et les données hexadécimales. La primitive de base provenant de u_display_hex_2 est:

    function f_display_hex(p_ptPointerp_countp_columnsInteger;
        p_set_of_hex_typet_set_of_hex_type): String;

le dernier paramètres indiquant simplement le type de données à afficher (l'adresse, la partie hexadécimale, la partie ASCII)

Les variables sont déclarées par:

    var g_ci_squareci_squareNil;
        g_i_computei_compute;
        g_i_journali_journal;
        g_i_unknowniUnknown;

et créées par:

    procedure TForm1.create_Click(SenderTObject);
      begin
        g_ci_square:= ci_square.create;
        g_i_unknown:= g_ci_square as iUnknown;
        g_i_compute:= g_ci_square as i_compute;
        g_i_journal:= g_ci_square as i_journal;
      end// create_Click

Nous pouvons connaître les valeurs des pointeurs d'objet et d'Interface par

        display('object 'f_integer_to_hex(Integer(g_ci_square)));
        display('iUnk   'f_integer_to_hex(Integer(g_i_unknown)));
        display('i2     'f_integer_to_hex(Integer(g_i_journal)));
        display('i1     'f_integer_to_hex(Integer(g_i_compute)));

Ce qui nous donne:

 
          object 008D.6DE8 
          iUnk   008D.6DF0 
          i2     008D.6DFC 
          i1     008D.6E00 
          

Les données de l'objet g_ci_square sont affichées par:

        display(f_display_hex(g_ci_square, 4+ 4+ 4+ 8+ 2* 4, 4, k_hex_address_hex));

soit:

 
          8D6DE8: 34 35 46 00  : < 
          8D6DEC: 03 00 00 00  : < 
          8D6DF0: BC 11 40 00  : < 
          8D6DF4: 11 11 00 00  : < 
          8D6DF8: 22 22 00 00  : < 
          8D6DFC: 84 34 46 00  : < 
          8D6E00: 98 34 46 00  : < 
          

Nous vérifions bien que:

  • l'objet pointe vers le début de la zone. C'est là que se trouve le pointeur vers la VMT ($46.3534)
  • ensuite nous trouvons le compte de référence 3: nous avons créé trois variables Interface)
  • puis nous trouvons le pointeur vers l'IMT de iUnknown, les données, les pointeurs vers les deux autres IMTs
Nous affichons la VMT en utilisant le pointeur situé au début de l'objet:

    type t_vmtarray[0..0] of Integer;
         t_pt_vmt= ^t_vmt;

    procedure display_the_vmt(p_pt_objectPointer);
      var l_pt_vmtt_pt_vmt;
      begin
        l_pt_vmt:= t_pt_vmt(p_pt_object^);
        display(f_display_hex(Pointer(Integer(l_pt_vmt)- k_vmt_entries* 4),
                k_vmt_entries* 4+ 4,  4, k_hex_address));
      end// display_the_vmt

    ...
    display_the_vmt(g_ci_square);

La constante k_vmt_entries indique combien de données se trouvent dans la VMT avant nos procédures: Delphi y places les données pour être compatible avec COM, ainsi que des données propres à la Class: en -10 la taille des instances ($1C) et en -1 l'adresse de Destroy, etc. Le nombre de ces entrées est de 19. La VMT est alors la suivante:

 
          4634E8: 34 35 46 00  : 45F.< 
          4634EC: AC 34 46 00  : ¬4F.< 
          4634F0: 00 00 00 00  : ....< 
          4634F4: 00 00 00 00  : ....< 
          4634F8: 00 00 00 00  : ....< 
          4634FC: 00 00 00 00  : ....< 
          463500: 00 00 00 00  : ....< 
          463504: 00 00 00 00  : ....< 
          463508: 3C 35 46 00  : <5F.< 
          46350C: 1C 00 00 00  : ....< 
          463510: E8 11 40 00  : è.@.< 
          463514: 1C 3C 40 00  : .<@.< 
          463518: 2C 65 40 00  : ,e@.< 
          46351C: 38 65 40 00  : 8e@.< 
          463520: 30 3C 40 00  : 0<@.< 
          463524: 24 3C 40 00  : $<@.< 
          463528: 48 65 40 00  : He@.< 
          46352C: 88 39 40 00  : ê9@.< 
          463530: 48 36 46 00  : H6F.< 
          463534: F0 35 46 00  : ð5F.< 
          

Et pour terminer les IMT. Le contenu d'une IMT se fait comme pour les VMT. Dans le cas de i_compute, par exemple, nous obtenons:

 
          i1 008D.6E00 
          463498: 63 34 46 00  : < 
          46349C: 6D 34 46 00  : < 
          4634A0: 77 34 46 00  : < 
          4634A4: 4F 34 46 00  : < 
          4634A8: 59 34 46 00  : < 
          

Nous y trouvons:

  • les adresses des thunks de QueryInterface, Addref et Release
  • les adresses des thunks des deux méthodes
Et pour les thunks c'est un peu plus compliqué:
  • au début se trouve le calcul de différence d'adresse
  • puis se trouve l'adresse de JMP: si nous additionnons l'adresse du début du JMP nous trouvons l'adresse de la méthode
Voici le résultat de ce calcul pour le thunk de notre première méthode:

 
          method_3 0046.3594 
          46344F: 83 44 24 04  : âD$.< 
          463453: E8 E9 3B 01  : èé;.< 
          463457: 00 00 ______ : ..__< 
          jmp 0046.3594 
          

Mentionnons que le thunk présenté ci-dessus correspond à une FUNCTION ayant l'attribut StdCall. Pour une PROCEDURE normale les valeurs sont différentes (nous le présenterons ci-dessous).

Et comme au début du thunk se trouve l'addition, nous pouvons même calculer l'adresse de l'objet à partir de l'Interface:

 
          itf_to_object 
          itf     008D.6E00 
          + delta 0000.00E8 
          =       008D.6EE8 
          object  008D.6DE8 
          

3.5 - Désassemblage

Pour ceux qui souhaitent vérifier la partie assembleur, nous avons regardé le code d'appel des méthodes à l'aide des objet ou des variables Interface. Voici les appels:

    procedure TForm1.call_Click(SenderTObject);
      begin
        g_ci_square.set_side($3333);
        g_i_compute.set_side($4444);

        g_ci_square.write_and_display;
        g_i_journal.write_and_display;
      end// call_Click

Nous avons alors placé un point d'arrêt et examiné le code assembleur (Voir | Débug | Registers ). Voici pour les appels:

et pour les thunks de relocation:


4 - Affichage Commenté

4.1 - Affichage Commenté

Ayant compris l'organisation générale, nous avons ajouté à notre programme un affichage avec commentaire en face de chaque ligne. Nous avons juste ajouté une unité qui permet de recalculer les thunks:

unit u_vmt_imt;
  interface

    type t_vmtarray[0..0] of Integer;
         t_pt_vmt= ^t_vmt;

    const k_vmt_entries= 19;

          k_com_vmt_index= - 19;
          k_com_imt_index= - 18;
          k_object_size_index= - 10;
          k_destroy_index= -1;

    procedure display_vmt(p_pt_objectPointerp_virtual_method_countInteger);
    function f_imt_function_jump_value(p_pt_imtPointerp_method_indexInteger): Integer;
    function f_imt_procedure_jump_value(p_pt_imtPointerp_method_indexInteger): Integer;

  implementation
    uses SysUtilsu_c_displayu_display_hex_2;

    procedure display_vmt(p_pt_objectPointerp_virtual_method_countInteger);
      begin
        display(f_display_hex(Pointer(p_pt_object^), 4* p_virtual_method_count, 4, k_hex_address_hex));
      end// display_vmt

    const k_add_byte_ptr_esp= $04244483;
          k_jump= $E9;
    type t_function_thunkpacked record
                                  m_add_byte_ptr_esplongint;
                                  m_deltabyte;
                                  m_jumpByte;
                                  m_jump_to_function_startlongint;
                                end;
         t_pt_function_thunk= ^t_function_thunk;

    function f_pt_function_thunk(p_pt_imtPointerp_method_indexInteger): t_pt_function_thunk;
      begin
        (*$r-*)
        Result:= t_pt_function_thunk(t_pt_vmt(p_pt_imt^)[p_method_index]);
        (*$r+*)
      end// f_pt_function_thunk

    function f_is_imt_function_thunk(p_pt_imtPointerp_method_indexInteger): Boolean;
      begin
        with f_pt_function_thunk(p_pt_imtp_method_index)^ do
          Result:= (m_add_byte_ptr_espk_add_byte_ptr_espand (m_jumpk_jump);
      end// f_is_imt_function_thunk

    function f_imt_function_jump_value(p_pt_imtPointerp_method_indexInteger): Integer;
      var l_pt_function_thunkt_pt_function_thunk;
          l_function_addressInteger;
      begin
        if f_is_imt_function_thunk(p_pt_imtp_method_index)
          then begin
              l_pt_function_thunk:= f_pt_function_thunk(p_pt_imtp_method_index);
              // -- compute the absolute adress, starting from the start of "JMP XXX"
              Result:= $0A
                  + Longint(l_pt_function_thunk)
                  + l_pt_function_thunk^.m_jump_to_function_start;
            end
          else begin
              display('  not_thunk');
              Result:= 0;
            end;
      end// f_imt_function_jump_value

    // -- procedure

    const k_add= $C083;
    type t_procedure_thunkpacked record
                                  m_addWord;
                                  m_deltaByte;
                                  m_jumpByte;
                                  m_jump_to_procedure_startlongint;
                                end;
         t_pt_procedure_thunk= ^t_procedure_thunk;

    function f_pt_procedure_thunk(p_pt_imtPointerp_method_indexInteger): t_pt_procedure_thunk;
      begin
        (*$r-*)
        Result:= t_pt_procedure_thunk(t_pt_vmt(p_pt_imt^)[p_method_index]);
        (*$r+*)
      end// f_pt_procedure_thunk

    function f_is_imt_procedure_thunk(p_pt_imtPointerp_method_indexInteger): Boolean;
      begin
        with f_pt_procedure_thunk(p_pt_imtp_method_index)^ do
          Result:= (m_addk_addand (m_jumpk_jump);
      end// f_is_imt_procedure_thunk

    function f_imt_procedure_jump_value(p_pt_imtPointerp_method_indexInteger): Integer;
      var l_pt_procedure_thunkt_pt_procedure_thunk;
          l_procedure_addressInteger;
      begin
        if f_is_imt_procedure_thunk(p_pt_imtp_method_index)
          then begin
              l_pt_procedure_thunk:= f_pt_procedure_thunk(p_pt_imtp_method_index);
              // -- compute the absolute adress, starting from the start of "JMP XXX"
              Result:= $08
                  + Longint(l_pt_procedure_thunk)
                  + l_pt_procedure_thunk^.m_jump_to_procedure_start;
            end
          else begin
              display('  not_thunk');
              Result:= 0;
            end;
      end// f_imt_procedure_jump_value

  end.

Pour afficher les IMTs, nous utilisons, par exemple:

    type t_call_type= (e_functione_procedure);

    procedure display_imt_entry(p_pointerPointerp_method_indexInteger;
        p_call_typet_call_type;
        p_titleString);
      var l_addressInteger;
          l_thunk_addressInteger;
          l_code_addressInteger;
      begin
        l_address:= Integer(p_pointer^)+ p_method_index* 4;
        l_thunk_address:= Integer(Pointer(l_address)^);

        // -- now find the code address in the thunk
        case p_call_type of
          e_function : l_code_address:= f_imt_function_jump_value(p_pointerp_method_index);
          e_procedure : l_code_address:= f_imt_procedure_jump_value(p_pointerp_method_index);
          else l_code_address:= 0;
        end// p_call_type

        display(f_display_hex(Pointer(l_address), 4, 4, k_hex_address_hex)
            + '= 'f_integer_to_hex(l_thunk_address)
            + ' ['f_integer_to_hex(l_code_address)+ ']'
              + '  'p_title);
      end// display_imt_entry

    procedure display_i_journal;
      begin
        display_line;
        display('i2 'f_integer_to_hex(Integer(g_i_journal)));

        display_imt_entry(Pointer(g_i_journal), 0, e_function'i_journal.QueryInterface');
        display_imt_entry(Pointer(g_i_journal), 1, e_function'i_journal.Addref');
        display_imt_entry(Pointer(g_i_journal), 2, e_function'i_journal.Release');
        display_imt_entry(Pointer(g_i_journal), 3, e_procedure'i_journal.write_to_log');
      end// display_i_journal

Le reste des procédures est similaire. Vous en trouverez le détail dans le projet téléchargeable.

Le résultat pour notre Classe est:

 
          iUn 008D.6DF0 
          4011BC: 9D 11 40 00  : <= 0040.119D [0040.6558]  iUnknown.QueryInterface 
          4011C0: A7 11 40 00  : <= 0040.11A7 [0040.6580]  iUnknown.Addref 
          4011C4: B1 11 40 00  : <= 0040.11B1 [0040.6594]  iUnknown.Release 
          

i2 008D.6DFC 463484: 31 34 46 00 : <= 0046.3431 [0040.6558] i_journal.QueryInterface 463488: 3B 34 46 00 : <= 0046.343B [0040.6580] i_journal.Addref 46348C: 45 34 46 00 : <= 0046.3445 [0040.6594] i_journal.Release 463490: 1D 34 46 00 : <= 0046.341D [0046.35D0] i_journal.write_to_log

i1 008D.6E00 463498: 63 34 46 00 : <= 0046.3463 [0040.6558] g_i_compute.QueryInterface 46349C: 6D 34 46 00 : <= 0046.346D [0040.6580] g_i_compute.Addref 4634A0: 77 34 46 00 : <= 0046.3477 [0040.6594] g_i_compute.Release 4634A4: 4F 34 46 00 : <= 0046.344F [0046.3594] g_i_compute.set_side 4634A8: 59 34 46 00 : <= 0046.3459 [0046.35AC] g_i_compute.compute_area

methods 463548: 55 8B EC 83 : <= ci_square.create 463594: 55 8B EC 51 : <= ci_square.set_side 4635AC: 55 8B EC 51 : <= ci_square.compute_area 4635D0: 55 8B EC 51 : <= ci_square.write_to_journal 4635F0: 55 8B EC 51 : <= ci_square.write_and_display 463620: 55 8B EC 51 : <= ci_square.analyze 463634: 55 8B EC 51 : <= ci_square.analyze_virtual 463648: 55 8B EC 83 : <= ci_square.destroy

ci_square vmt 0046.3534 46350C: 1C 00 00 00 : <= ci_area_vmt[-$A0] object_size ... 463530: 48 36 46 00 : <= ci_area_vmt[-$01] vm 0 Destroy 463534: F0 35 46 00 : <= ci_area_vmt[ $00] vm 0 write_and_display 463538: 34 36 46 00 : <= ci_area_vmt[ $01] vm 0 analyze_virtual

object 8D6DE8: 34 35 46 00 : < 0046.3534 <= pt_vmt 8D6DEC: 03 00 00 00 : < 0000.0003 <= reference_count 8D6DF0: BC 11 40 00 : < 0040.11BC <= pt_imt_iUnknown 8D6DF4: 11 11 00 00 : < 0000.1111 <= m_side (= $1111) 8D6DF8: 22 22 00 00 : < 0000.2222 <= m_area (= $2222) 8D6DFC: 84 34 46 00 : < 0046.3484 <= pt_imt_journal 8D6E00: 98 34 46 00 : < 0046.3498 <= pt_imt_compute

object 008D.6DE8 iUnk 008D.6DF0 i2 008D.6DFC i1 008D.6E00


5 - Améliorations

Notre programme avait uniquement pour objectif de vérifier l'organisation mémoire des Classes et Interfaces Delphi.

Ce programme a été spécialisé pour analyser notre Classe particulière. Nous pourrions généraliser ce programme pour afficher le contenu mémoire correspondant à n'importe quelle Classe ou Interface qui serait fournie en paramètre.


6 - Télécharger le source

Nous avons placé le projet dans des .ZIP qui comprend:
  • le .DPR, la forme principale, les formes annexes éventuelles
  • les fichiers de paramètres (le schéma et le batch de création)
  • dans chaque .ZIP, toutes les librairies (Unit) nécessaires à chaque projet (chaque .ZIP est autonaume)
Ces .ZIP contiennent des chemins RELATIFS. Par conséquent:
  • créez un répertoire n'importe où sur votre machine
  • placez le .ZIP dans ce répertoire
  • dézippez et les sous-répertoires nécessaires seront créés
  • compilez et exécutez
Au niveau sécurité:
  • les .ZIP:
    • ne modifient pas votre PC (pas de changement de la Base de Registre, d'écrasement de DLL ou autre .BAT). Aucune modification de répertoire ou de contenu de répertoire ailleurs que dans celui où vous dézippez
    • ne contiennent aucun programme qui s'exécuterait à la décompression (.EXE, .BAT, .SCR ou autre .VXD) ou qui seraient lancés plus tard (reboot)
    • passez-les à l'antivirus avant décompression si vous êtes inquiets.
  • les programmes ne changent pas la base de registre et ne modifient aucun autre répertoire de votre machine
  • pour supprimer le projet, effacez simplement le répertoire.
Voici le .ZIP:
  • dump_interface.zip: le projet d'affichage des Classes et Interfaces (3 K)

    Ce zip contient

    • le projet
    • l'unité u_ci_area avec la Classe et les deux Interfaces
    • l'unité u_vmt_imt
    • l'unité u_display_hex_2
    • les autres unités auxiliaires (affichage etc)


Comme d'habitude:
  • nous vous remercions de nous signaler toute erreur, inexactitude ou problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections qui en résulteront pourront aider les prochains lecteurs
  • tous vos commentaires, remarques, questions, critiques, suggestion d'article, ou mentions d'autres sources sur le même sujet seront de même les bienvenus à jcolibri@jcolibri.com.
  • plus simplement, vous pouvez taper (anonymement ou en fournissant votre e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en cliquant "envoyer" :
    Nom :
    E-mail :
    Commentaires * :
     

  • et si vous avez apprécié cet article, faites connaître notre site, ajoutez un lien dans vos listes de liens ou citez-nous dans vos blogs ou réponses sur les messageries. C'est très simple: plus nous aurons de visiteurs et de références Google, plus nous écrirons d'articles.

7 - Conclusion

Notre petit projet nous permet donc de vérifier que
  • une variable objet est un pointeur. Ce pointeur pointe vers la zone des donnée, contenant au début le pointeur vers la VMT, puis les données membre de l'objet. Cette architecture est fort classique, et n'a pas bougé depuis 1991 !
  • une variable de type Interface est un pointeur. Ce pointeur pointe à l'intérieur de la zone de l'objet qui implémente la variable Interface. Cette zone est un pointeur vers la table des méthodes correspondant à l'Interface. D'où l'expression "un objet COM est un pointeur de pointeur désignant une VMT". Sauf que:
    • la "VMT du COM" n'est pas la même que celle de l'objet: c'est une IMT
    • il y a autant d'IMT que d'Interfaces implémentées par notre Class
    • chacune de ces IMT reprend toutes les entrées de ses Interfaces ancêtre, et au minimum les 3 entrées de iUnknown
    • le contenu des ces IMT ne sont pas des adresses de méthodes, mais des adresses de thunk de relocation qui désignent, eux, les méthodes
    • le décalage contenu dans le thunk de relocation permet éventuellement de calculer un pointeur vers l'objet à partir d'une variable Interface
    • les IMT sont situés avant la VMT
    • la VMT contient la taille de chaque instance
  • contrairement à ce que je croyais:
    • les méthodes d'une Interface ne sont pas obligatoirement et systématiquement implémentées sous forme de méthodes Virtual. L'Interface peut être considérée comme constituée de méthodes Virtual Abstract, mais l'implémentation contient des méthodes qui sont Virtual ou non
    • même pour les Interfaces COM, les méthodes ne sont pas systématiquement des Function ayant pour résultat hResult et comme attribut StdCall. Nous pouvons utiliser des Procédures et le mode d'appel est indifférent.

8 - Références

  • Delphi Component Design
        Danny THORPE - Addison Wesley Isbn 0-201-46136-6 - 1997
  • Delphi COM
        Eric HARMON - New Riders Isbn 1-57870-221-6 - 2000
  • Programmation Objet Pascal
        John COLIBRI - L'Institut Pascal - 1989
  • Client et Serveur COM
        John COLIBRI - Pascalissime 66 - Avril 1997

9 - L'auteur

John COLIBRI est passionné par le développement Delphi et les applications de Bases de Données. Il a écrit de nombreux livres et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le conseil (composants, architecture, test) et la formation. Son site contient des articles avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client.
Created: jan-04. Last updated: mar-2020 - 250 articles, 620 .ZIP sources, 3303 figures
Contact : John COLIBRI - Tel: 01.42.83.69.36 / 06.87.88.23.91 - email:jcolibri@jcolibri.com
Copyright © J.Colibri   http://www.jcolibri.com - 2001 - 2020
Retour:  Home  Articles  Formations  Développement Delphi  Livres  Pascalissime  Liens  Download
l'Institut Pascal

John COLIBRI

+ Home
  + articles_avec_sources
    + bases_de_donnees
    + web_internet_sockets
    + services_web_
    + prog_objet_composants
      – dump_interface
      – packages_delphi
      – ecriture_de_composant
      – c_list_of_double
      – interfaces_delphi
      – delphi_generics
      – delphi_rtti
    + office_com_automation
    + colibri_utilities
    + uml_design_patterns
    + graphique
    + delphi
    + outils
    + firemonkey
    + vcl_rtl
    + colibri_helpers
    + colibri_skelettons
    + admin
  + formations
  + developpement_delphi
  + présentations
  + pascalissime
  + livres
  + entre_nous
  – télécharger

contacts
plan_du_site
– chercher :

RSS feed  
Blog

Prestataire Delphi réalisation de projets Delphi, création de composants, migration Delphi - Tél 01.42.83.69.36
Formation Bases de Données Multi Tiers Delphi Gestion de bases de données Mulit Tiers : architecture, le serveur d'application : dbExorss et DataSnap, les clients légers, édition d'états - 3 jours