IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Composants VCL Delphi Discussion :

Comment trouver l'adresse du canvas d'un TIMAGE ?


Sujet :

Composants VCL Delphi

  1. #1
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 287
    Points : 164
    Points
    164
    Billets dans le blog
    1
    Par défaut Comment trouver l'adresse du canvas d'un TIMAGE ?
    Je développe des fonctions spécifiques dans une DLL, en Delphi 6 Personal Edition, sous XP, W7 et W8 toutes versions. Et je rencontre un problème particulier avec un TImage passé en paramètre à une de mes fonctions.

    Je déclare ma fonction ainsi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    function GetCanvasAddress(pic: TObject):integer; stdcall; export;
    begin
    showmessage('a1 '+pic.ClassName); // <========== ici, j'ai bien TImage comme nom de classe !
     
      case TImage(pic).Picture.Bitmap.PixelFormat of
        pf1bit: showmessage('1 bits');
        pf4bit: showmessage('4 bits');
        pf8bit: showmessage('8 bits');
        pf15bit: showmessage('15 bits');
        pf16bit: showmessage('16 bits');
        pf24bit: showmessage('24 bits');
        pf32bit: showmessage('32 bits');
        pfCustom: showmessage('pfCustom');
        pfDevice: showmessage('pfDevice');   // <======== c'est ce format qui semble être reconnu, alors que ça devrait être 24 ou 32 bits !
      else
        showmessage('? bits');
      end;
    ...
    end;
    Déjà, le pixelformat n'est pas reconnu correctement. Et je ne peux pas faire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    ...
    line := TImage(pic).Picture.Bitmap.Scanline[0]; // <=========== crash ici !
    ...
    Je peux affirmer que la fonction de la DLL s'exécute dans le thread principal du programme appelant.

    Question: quel est la structure, le "layout" de l'objet TImage qui est passé à ma fonction ? J'ai "dumpé" les 64 premier integer 32 bits à partir de l'adresse que représente "pic", mais je ne sais pas interpréter le contenu. J'ai seulement réussi à identifier, avec certitude, les deux offsets suivants:
    mot 18 (offset 72) = largeur du TImage, en pixels
    mot 19 (offset 76) = hauteur du TImage, en pixels

    J'aimerais savoir comment trouver un pointeur, l'adresse du canvas associé au TImage, afin d'interagir avec le canvas, sans avoir à passer par les méthodes Pixels ou Scanline, qui ne fonctionnent pas dans mon contexte. Je sais comment est structuré un canvas, en fonction du pixelformat. J'ai juste besoin de savoir comment trouver l'adresse de début du canvas, à partir de la valeur qui représente l'objet TImage et qui est en réalité un pointeur vers une structure décrivant le TImage.

  2. #2
    Membre émérite
    Avatar de ALWEBER
    Homme Profil pro
    Expert Delphi
    Inscrit en
    Mars 2006
    Messages
    1 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 68
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Expert Delphi

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 483
    Points : 2 734
    Points
    2 734
    Billets dans le blog
    10
    Par défaut procedure à suivre
    Bonjour,

    Pour résoudre ta problématique je pense qu'il faut décomposer le problème. Déjà faire fonctionner ton ensemble dans un seul programme Delphi VCL puis après extraire la partie DLL si c'est possible.

    Peux tu nous donner plus d'info sur l'usage souhaité ou un mini-exemple

  3. #3
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 287
    Points : 164
    Points
    164
    Billets dans le blog
    1
    Par défaut
    Difficile. Ma fonction, écrite en Delphi 6 Personal Edition, est appelée par un programme externe non-Delphi. Mais je vais fournir autant de précisions que possible:

    Voici ma fonction:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    function GetCanvasAddress(pic: TObject):integer; stdcall; export;
    begin
    // début du code uniquement pour la mise au point:
    showmessage('a1 '+pic.ClassName); // <========== ici, j'ai bien TImage comme nom de classe !
      case TImage(pic).Picture.Bitmap.PixelFormat of
        pf1bit: showmessage('1 bits');
        pf4bit: showmessage('4 bits');
        pf8bit: showmessage('8 bits');
        pf15bit: showmessage('15 bits');
        pf16bit: showmessage('16 bits');
        pf24bit: showmessage('24 bits');
        pf32bit: showmessage('32 bits');
        pfCustom: showmessage('pfCustom');
        pfDevice: showmessage('pfDevice');   // <======== c'est ce format qui semble être reconnu, alors que ça devrait être 24 ou 32 bits !
      else
        showmessage('? bits');
      end;
    // fin du code pour la mise au point
    result := integer(TImage(pic).Picture.Bitmap.Scanline[0]);  // <========== ici, crash sur Scanline !
    end;
    Ma fonction est appelée par quelque chose d'équivalent à:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    var
      MyPicture: TImage;
      address: integer:
      ..
      address := GetCanvasAddress(MyPicture);
      ...
    Bon, le nom de la fonction est certainement mal choisi - ce n'est que provisoire. Ce que je veux, c'est trouver l'adresse du premier pixel dans la bitmap associée au TImage. Normalement, c'est ce que retourne Scanline[0].

    J'ai essayé de décoder la structuure du TImage, en faisant un dump de 64 mots à partir de l'adresse que représente le paramètre pic. Car en effet, un TObject n'est rien d'autre qu'une adresse pointant vers la structure décrivant l'objet.

    Je n'ai pu identifier avec certitude que 2 valeurs: la largeur et la hauteur du TImage, respectivement à l'offset 72 et 76 (décimal), à partir de l'adresse que représente l'objet pic. Mais il y a avec certitude quelque part, l'adresse de la bitmap associée, où l'on doit trouver ensuite l'adresse du premier pixel de la bitmap. Ou alors l'adresse d'un pointeur qui y mène...

    Comme je ne peu pas utiliser Scanline pour déterminer mon pointeur, je veux trouver cette adresse autrement, pour pouvoir travailler directement avec la bitmap. Je connais par ailleurs le pixelformat de mon TImage (bien que ce soit certainement codé également quelque part dans la structure du TImage ou de la bitmap associée) et je sais identifier la largeur et la hauteur. Donc, si j'ai l'adresse, je saurai traiter la bitmap.

  4. #4
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 430
    Points
    28 430
    Par défaut
    ce que tu tentes de faire n'est pas possible.

    1) className

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    class function TObject.ClassName: ShortString;
    begin
      Result := PShortString(PPointer(Integer(Self) + vmtClassName)^)^;
    end;
    ça fonctionne à condition que la DLL et l'EXE ont bien la même version du compilateur, c'est à dire la même structure de TObject, sinon plantage probable ou tout au moins valeur incorrecte

    2) Picture
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    property Picture: TPicture read FPicture
    là encore c'est un accès direct à un emplacement mémoire dans l'instance, ça peux fonctionner avec la même condition de compilateur

    3) Picture.Bitmap

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    function TPicture.GetBitmap: TBitmap;
    begin
      ForceType(TBitmap);
      Result := TBitmap(Graphic);
    end;
    là ça ne va plus !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    procedure TPicture.ForceType(GraphicType: TGraphicClass);
    begin
      if not (Graphic is GraphicType) then
      begin
        FGraphic.Free;
        FGraphic := nil;
        FGraphic := GraphicType.Create;
        FGraphic.OnChange := Changed;
        FGraphic.OnProgress := Progress;
        Changed(Self);
      end;
    end;
    Graphic est un accès direct au membre FGraphic, pas de soucis, mais GraphicType pointe sur la déclaration TBitmap de la DLL qui n'est donc pas la même déclaration que TBitmap dans l'exe - même si c'est le même code à l'octet près, ils sont distincts et donc considérés comme différents.

    du coup, descruction d'un objet par la DLL alors qu'il a été créé par l'exe, plantage très probable. Si toute fois ça ne devait pas planter on alloue un TBitmap déclaré dans la DLL qu'on vient coller dans le Image.Picture de l'exe, gros mélange de gestionnaires de mémoires qui décuple les probabilités de plantage.

    Non pour faire ce genre de chose, il faut passer soit par Image.Picture.Bitmap.Handle qui est une HBitmap géré par Windows, soit créer une Interface pour manipuler le bitmap

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    type
      IPicture = interface
        function PixelFormat: TPixelFormat;
        function ScanLine(Line: Integer): Pointer;
       ...
      end;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  5. #5
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 287
    Points : 164
    Points
    164
    Billets dans le blog
    1
    Par défaut
    Merci, Paul. Je vois que j'ai été à côté de la plaque.

    La solution par le handle n'est pas possible, malheureusement. Reste la solution par l'interface. Mais, j'avoue que je ne comprends pas comment la mettre en oeuvre dans mon cas. J'ai fait une mini-dll, dans laquelle il y a uniquement la fonction en question, et un programme de test avec les éléments suivants:
    1. une fonction locale locale au programme de démo contenant la copie intégrale de la fonction de la DLL
    2. un TImage dans lequel je dessine un carré rouge et du texte (ça pourrait être n'importe quoi)
    3. un bouton "Local: Get address" utilisant la fonction locale
    4. un bouton "DLL: Get address" utilisant la fonction de la DLL

    Le bouton "Local: Get address" fonctionne parfaitement.
    Le bouton "DLL: Get address" produit le phénomène que j'ai décrit et que tu as expliqué parfaitement.

    Alors, j'ai fait un fichier ZIP contenant les deux projets:
    - test_GetPictureAddress.dpr pour le programme de test
    - KGF.dpr pour la mini-dll

    Je suppose que l'interface doit être créé et mis en oeuvre dans la DLL. Mais comment ? Voudrais-tu bien me le montrer ? Merci d'avance !
    Fichiers attachés Fichiers attachés

  6. #6
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 287
    Points : 164
    Points
    164
    Billets dans le blog
    1
    Par défaut
    Auparavant, j'avais dit:
    J'ai essayé de décoder la structuure du TImage, en faisant un dump de 64 mots à partir de l'adresse que représente le paramètre pic. Car en effet, un TObject n'est rien d'autre qu'une adresse pointant vers la structure décrivant l'objet.

    Je n'ai pu identifier avec certitude que 2 valeurs: la largeur et la hauteur du TImage, respectivement à l'offset 72 et 76 (décimal), à partir de l'adresse que représente l'objet pic. Mais il y a avec certitude quelque part, l'adresse de la bitmap associée, où l'on doit trouver ensuite l'adresse du premier pixel de la bitmap. Ou alors l'adresse d'un pointeur qui y mène...
    Plus j'y pense, plus j'y trouve un intérêt. Certes, ce n'est pas très "orthodoxe". C'est un peu du "hacking". Mais j'assume. Je sais que tout se passe dans un seul stream, dans le mainstream du programme principal. J'ai vérifié: toutes les adresses fournies par le programme principal sont valides et accessibles dans la DLL. Pourquoi je ne pourrais pas, en suivant de lien en lien, finir par trouver l'adresse de début de la bitmap en mémoire ? Il me manque le "layout" de l'objet TImage, et éventuellement de la ou des structures à parcourir pour trouver l'adresse que retournerait Scanline[0].

    Est-ce que cette idée est si éloignée de ce qui est faisable ?

  7. #7
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 671
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 671
    Points : 13 065
    Points
    13 065
    Par défaut
    Scanline[0] n'est pas le premier pixel, il se trouve effectivement en bas à gauche de l'image, soit Scanline[Height -1].

  8. #8
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 287
    Points : 164
    Points
    164
    Billets dans le blog
    1
    Par défaut
    D'accord, Andnotor. Scanline[0] donne la ligne du "haut" de l'image.

    Mais comment puis-je trouver cette adresse, que ce soit Scanline[0] ou Scanline[Height-1], étant donné que je ne peux pas utiliser Scanline, dans la DLL ? JE sais trouver la hauteur, de la manière suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function GetPictureAddress(pic: TObject):integer; stdcall; export;
    var
      hauteur, largeur: integer;
      p: pinteger;
    begin
      largeur := pinteger(integer(pic)+72)^; 
      hauteur := pinteger(integer(pic)+76)^; 
      ...
      result := integer(...);   // <======= ici, je veux retourner la valeur de Scanline[Hauteur-1]
    end;
    Comment trouver ja bitmap, à partir des informations contenues dans la structure pointée par pic (qui est un TImage) ? Pour le moment, j'ai seulement réussi à identifier les offsets 72 et 76.

  9. #9
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 287
    Points : 164
    Points
    164
    Billets dans le blog
    1
    Par défaut
    EDIT du 14/5/2015 à 15:55:

    J'ai avancé un peu dans le décryptage de la structure pointée par la valeur qui est transmise à ma fonction, via le paramètre "pic: TObject". Voici les offsets que j'arrive à interpréter pour le moment:
    format de la table: m(o)=s m=numéro du mot (décimal) o=offset (décimal) s=signification
    0(0)=...
    1(4)=pointeur vers objet parent
    2(8)=adresse du nom de l'objet en format ASCIIZ
    3(12)=Tag
    4(16)=...
    5(20)=...
    6(24)=...
    7(28)=...
    8(32)=...
    9(36)=...
    10(40)=...
    1144)=...
    12(48)=pointeur vers objet parent
    13(52)=...
    14(56)=WindowProc adresse de la procedure
    15(60)=WindowProc adresse du nom de l'objet en format ASCIIZ
    16(64)=Left (en pixels)
    17(68)=Top (en pixels)
    18(72)=Width (en pixels)
    19(76)=Height (en pixels)
    20(80)=flags $00000040=Stretch
    21(84)=flags $01000000=Visible
    22(88)=flags $00000001=Enabled
    23(92)=flags $00000001=AutoSize
    ...
    32(128)=adresse du hint en format ASCIIZ
    ...
    38(132)=flags $00000100=ShowHint=True
    38(132)=flags $00010000=ParentShowHint=False
    ...
    52(208)=OnMouseDown adresse de la procédure
    53(212)=OnMouseDown adresse du nom de l'objet en format ASCIIZ
    54(216)=OnMouseMove adresse de la procédure
    55(220)=OnMouseMove adresse du nom de l'objet en format ASCIIZ
    56(224)=OnMouseUp adresse de la procédure
    57(228)=OnMouseUp adresse du nom de l'objet en format ASCIIZ
    58(232)=OnDragDrop adresse de la procédure
    59(236)=OnDragDrop adresse du nom de l'objet en format ASCIIZ
    60(240)=OnDragOver adresse de la procédure
    61(244)=OnDragOver adresse du nom de l'objet en format ASCIIZ
    ...
    64(256)=OnStartDock adresse de la procédure
    65(260)=OnStartDock adresse du nom de l'objet en format ASCIIZ
    66(264)=OnEndDock adresse de la procédure
    67(268)=OnEndDock adresse du nom de l'objet en format ASCIIZ...
    68(272)=OnStartDrag adresse de la procédure
    69(276)=OnStartDrag adresse du nom de l'objet en format ASCIIZ
    70(280)=OnEndDrag adresse de la procédure
    71(284)=OnEndDrag adresse du nom de l'objet en format ASCIIZ
    72(288)=OnClick adresse de la procédure
    73(292)=OnClick adresse du nom de l'objet en format ASCIIZ
    74(296)=OnDblClick adresse de la procédure
    75(300)=OnDblClick adresse du nom de l'objet en format ASCIIZ
    76(304)=OnContextPopup adresse de la procédure
    77(308)=OnContextPopup adresse du nom de l'objet en format ASCIIZ
    ...
    92(368)=OnProgress adresse de la procédure
    93(372)=OnProgress adresse du nom de l'objet en format ASCIIZ

    Qui pourrait me donner la signification des autres mots ? Et parmi les mots non identifiés en-dessous de 16 ou au-dessus de 19, lequel pointe vers la Bitmap associée au TImage, même à travers (plusieurs) indirections ? Car ces valeurs, je peux les lire et interpréter dans la DLL. Je n'ai pas besoin d'utiliser les méthodes si le connais les offsets. Certes, j'ai conscience que ce n'est pas orthodoxe, et que cela risque de ne pas marcher avec d'autres versions du compilateur. En encore - pas sûr. A ce niveau-là, je pense que je suis en plein dans les structures de Windows.

  10. #10
    Membre émérite
    Avatar de ALWEBER
    Homme Profil pro
    Expert Delphi
    Inscrit en
    Mars 2006
    Messages
    1 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 68
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Expert Delphi

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 483
    Points : 2 734
    Points
    2 734
    Billets dans le blog
    10
    Par défaut La solution
    Bonjour

    La solution passe par une sérialisation de l'objet à traiter

    Ci joint l'exemple modifié :
    Test_getpictureaddress_alain.zip

    Si tu as besoin d'explication complémentaires n'hésites pas

  11. #11
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 287
    Points : 164
    Points
    164
    Billets dans le blog
    1
    Par défaut
    Bonjour, Alain,

    Ta solution est séduisante. Et elle marche. Merci d'avoir regardé mon problème !

    Mais il y a un souci: j'ai ma main au niveau de la DLL, puisque c'est moi qui l'écris. Je peux donc transformer ma fonction comme je le veux, et en particulier appliquer ta technique. Mais je n'ai pas la main au niveau du programme principal ! Le module test_... que j'ai fourni, est juste une "simulation". Le programme principal est un exécutable "fermé" dont je n'ai pas les sources. La seule chose dont je sois sûr, c''est que ma fonction est appelée avec un paramètre "pic: TImage". Je ne peux malheureusement pas imposer d'y substituer un memory stream.

    C'est pourquoi je cherche actuellement à décoder la structure du TImage qui est passée en paramètre. Dans mon post précédent, il y a la liste des éléments que j'ai réussi à identifier. Mais il manque l'essentiel: le lien vers le Picture qui est une propriété du TImage, puis le lien vers la Bitmap qui est une propriété du Picture. Car, je sais lire les mots et suivre les liens en les traitant comme des pointeurs - on est dans le même adressage virtuel. Je ne peux juste pas utiliser les propriétés Picture, Bitmap et par conséquant Scanline.

  12. #12
    Membre émérite
    Avatar de ALWEBER
    Homme Profil pro
    Expert Delphi
    Inscrit en
    Mars 2006
    Messages
    1 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 68
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Expert Delphi

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 483
    Points : 2 734
    Points
    2 734
    Billets dans le blog
    10
    Par défaut Une piste
    Citation Envoyé par KlausGunther Voir le message
    ...
    ma fonction est appelée avec un paramètre "pic: TImage".
    Tu peux peut être aller dans ce sens :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    function GetPictureAddress(pic: TImage):integer; stdcall; export;
    var
      n: integer;
      bmp1 : TBitmap ;
      ms1: TMemoryStream;
      pic1: TImage ;
      Picture1 : TPicture ;
    begin
    // début du code pour la mise au point:
      result := 0 ;
      showmessage('a1 '+pic.ClassName);
      showmessage(intToStr(pic.Picture.Height)) ;
      ms1 := TMemoryStream.Create ;
      pic.Picture.Graphic.SaveToStream(ms1);
      ms1.Position := 0 ;
      Picture1 := TPicture.Create ;
      Picture1.Bitmap := TBitmap.Create ;
      Picture1.Graphic.LoadFromStream(ms1);
      ms1.Free ;
      case Picture1.Bitmap.PixelFormat of

  13. #13
    Membre émérite
    Avatar de ALWEBER
    Homme Profil pro
    Expert Delphi
    Inscrit en
    Mars 2006
    Messages
    1 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 68
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Expert Delphi

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 483
    Points : 2 734
    Points
    2 734
    Billets dans le blog
    10
    Par défaut En complément
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    function GetPictureAddress(pic: TImage):integer; stdcall; export;
    var
      ms1: TMemoryStream;
      Picture1 : TPicture ;
    begin
      ms1 := TMemoryStream.Create ;
      pic.Picture.Graphic.SaveToStream(ms1);
      ms1.Position := 0 ;
      Picture1 := TPicture.Create ;
      Picture1.Bitmap := TBitmap.Create ;
      Picture1.Bitmap.PixelFormat := pic.Picture.Bitmap.PixelFormat ;
      Picture1.Graphic.LoadFromStream(ms1);
      ms1.Position := 0 ;
      pic.Picture.Graphic.LoadFromStream(ms1);
      pic.Picture.Bitmap.Canvas.Brush.Color := clBlue;
      pic.Picture.Bitmap.Canvas.Rectangle(0,0,100,100);
      pic.Picture.Bitmap.Canvas.TextOut(10,10,'Hello');
      ms1.Free ;
      result := 0 ;
    end;

  14. #14
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 287
    Points : 164
    Points
    164
    Billets dans le blog
    1
    Par défaut
    Merci de continuer à réfléchir à mon problème !

    J'ai installé la deuxième version de la fonction que tu as postée. Mais ça provoque une violation d'accès.

    J'ai donc épuré le code, et je l'ai truffé de messages. Voilà ce que ça donne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    function GetPictureAddress(pic: TImage):integer; stdcall; export;
    var
      ms1: TMemoryStream;
      Picture1 : TPicture ;
      n: integer;
    begin
    showmessage('a1');
      ms1 := TMemoryStream.Create ;
    showmessage('a2');
      pic.Picture.Graphic.SaveToStream(ms1);
    showmessage('a3');
      ms1.Position := 0 ;
    showmessage('a4');
      Picture1 := TPicture.Create ;
    showmessage('a5');
      Picture1.Bitmap := TBitmap.Create ;
    showmessage('a6');
      Picture1.Bitmap.PixelFormat := pic.Picture.Bitmap.PixelFormat ;
    showmessage('a7');
      Picture1.Graphic.LoadFromStream(ms1);
    showmessage('a8');
      ms1.Free ;
      showmessage('a9 ' + pic.ClassName);
      case Picture1.Bitmap.PixelFormat of
        pf1bit:
          showmessage('1 bits');
        pf4bit:
          showmessage('4 bits');
        pf8bit:
          showmessage('8 bits');
        pf15bit:
          showmessage('15 bits');
        pf16bit:
          showmessage('16 bits');
        pf24bit:
          showmessage('24 bits');
        pf32bit:
          showmessage('32 bits');
        pfCustom:
          showmessage('pfCustom');
        pfDevice:
          showmessage('pfDevice');
      else
        showmessage('? bits');
      end;
      n := Picture1.Bitmap.Height;
      showmessage('a1 '+inttostr(n) + ' lignes');
      result := integer(Picture1.Bitmap.Scanline[n-1]) ;
    end;
    Le plantage a lieu après le message "A3", sur la ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      pic.Picture.Graphic.SaveToStream(ms1);
    Et ça s'explique parfaitement par l'argumentation de Paul Toth. On ne peut malheureusement pas utiliser les propriétés de TImage, sur un TImage passé en paramètre à une DLL. C'est fort dommage, mais c'est ainsi. J'avais fait de multiples essais, avant d'appeler ce forum à l'aide, et je me suis heurté à un mur. Et Paul l'explique parfaitement.

    L'idée du Memory Stream est nouvelle, et séduisante. Cependant, la création du stream ne peut se faire que dans la DLL, à partir du TImage passé en paramètre. Et là, la propriété Picture du TImage ne marche plus. Dommage.

    Et le voilà ramené à mon point de réflexion de mes deux posts précédents: j'interprète le TImage passé en paramètre comme un pointeur (ce que c'est, en réalité !) vers une zone en mémoire où se trouve la structure décrivant le TImage. Et là, je peux lire cette adresse et toutes les adresses suivantes, en déréférençant mon pointer de type pinteger et en l'incrémentant chaque fois. Je peux ainsi parcourir la structure (jusqu'où, en fait ?) et interpréter les différents éléments. J'en ai identifié un certain nombre, mais il me manque le pointeur sur le TPicture associé au TImage (celui référencé par le propriété Picture). Il est à quel offset ? Et à partir de là, je veux trouver le pointeur sur la TBitMap du TPicture (propriété BitMap de Image.Picture !). Et ainsi, j'aurai ce qu'il faut pour traiter les pixels.

    Ce qu'il ne faut pas oublier, c'est que le veux traiter les pixels dans leur location mémoire d'origine, ce qui permettra de les remplacer. Travailler sur une copie revient à produire un "sens unique", une sorte de bitmap "read-only".

    Donc, je pense que le but sera vraiment de trouver des informations sur la structure d'un TImage en mémoire, de sorte à en extraire le pointer nécessaire et de le suivre jusqu'à la bitmap. Là, au moins, je ne rentre pas en conflit avec les instances différentes entre programme princpal et DLL. Je sais bien que je resterai prisonnier d'une seule version de Delphi, mais dans mon contexte, cela ne me dérange absolument pas.

  15. #15
    Membre émérite
    Avatar de ALWEBER
    Homme Profil pro
    Expert Delphi
    Inscrit en
    Mars 2006
    Messages
    1 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 68
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Expert Delphi

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 483
    Points : 2 734
    Points
    2 734
    Billets dans le blog
    10
    Par défaut
    Bonjour,

    J'ai effectué les essais en Delphi 7 et Delphi XE3. Apparemment tu utilises Delphi 6. Dans ces deux univers j'ai validé le fait que ce soit le contenu de l'objet et non le pointeur en utilisant la procédure "pic.Picture.SaveToFile('test.bmp')".

    Si le test est OK sur Delphi 6, il y a peut être une possibilité en créant un nouvel objet hérité de Timage et en rendant public la propriété Data.

    A suivre

  16. #16
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 287
    Points : 164
    Points
    164
    Billets dans le blog
    1
    Par défaut
    Je vais resituer le contexte, Alain.

    J'utilise effectivement Delphi 6 Personal Edition, pour créer une vaste DLL de soutien, d'extension, à un programme externe dont je n'ai pas les sources et sur lequel je n'ai aucune influence. J'ai des spécifications d'interface très strictes à respecter, et en aucun cas, je ne peux imposer quoi que ce soit au niveau des paramètres qui me sont passés.

    Voici quelques-unes des contraintes imposées:
    - je ne peux exporter que des fonctions, pas de procédures
    - toutes les fonctions exportées doivent être de type INTEGER et retourner une valeur entière sur 32 bits
    - toutes les fonctions exportées sont appelées par la convention STDCALL
    - une fonction exportée peut avoir entre 0 et 6 paramètres
    - aucun paramètre ne peut être optionnel
    - chaque paramètre est du type INTEGER et est passe "par valeur"
    - dans le programme principal, on a la possibilité de préciser qu'un paramètre doit être l'adresse d'une variable, un objet Windows, un handle d'un contrôle fenêtré ou un handle d'un canvas. Dans tous ces cas, ces informations sont passées sous forme d'une valeur INTEGER 32 bits, par valeur

    C'est pourquoi je sais que mon image est passée sous forme de "pic: TIMAGE". Je n'ai malheureusement pas la possibilité de passer "pic: TBitmap" car je n'ai pas accès à la propriété Picture.Bitmap de l'objet TImage que je veux transmettre. Il faut trouver un moyen de suivre les pointeurs à partir de la structure du TIMAGE justqu'au BITMAP.

    Dans la DLL, le ne peux pas utiliser pic.Picture - violation d'accès. Quelque soit la propriété de Picture que j'essaie d'utiliser.

    Au lieu de passer "pic: TImage", je pourrais passer "hnd: HDC" en passant la propriété Canvas.Handle de mon TImage. J'avais essayé, mais je n'avais pas non plus réussi à trouver la bitmap. C'est pour cette raison que je voulais repartir de l'analyse de la structure de l'objet TImage et suivre les pointeurs.

  17. #17
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 430
    Points
    28 430
    Par défaut
    tu peux tenter avec ceci si tu veux lire/enregistrer l'image:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    var 
     Persist: IStreamPersist;
     Stream: TMemoryStream;
    begin
      TObject(Pic).GetInterface(IStreamPersist, Persist);
      Stream := TMemoryStream.Create;
      Persist.SaveToSTream(Stream);
    end;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  18. #18
    Membre habitué

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 287
    Points : 164
    Points
    164
    Billets dans le blog
    1
    Par défaut
    Cela plante avec violation d'accès sur la ligne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      Persist.SaveToSTream(Stream);
    Je pense que la raison en est toujours la même, celle que tu as décrite.

    Qu'est-ce tu penses de ma tentative de décrypter la structure de l'objet TImage dont l'adresse est passée en paramètre ? As-tu des informations qui me manquent dans le tableau, et en particulier l'offset du pointeur vers la propriété TPicture du TImage, et celle du TBitmap du TPicture ? Car je peux accéder par un pointeur de type pinteger, à tous les offsets que je veux...

  19. #19
    Membre expérimenté Avatar de guillemouze
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    876
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Novembre 2004
    Messages : 876
    Points : 1 448
    Points
    1 448
    Par défaut
    Essaye peut etre juste de creer un bitmap qui pointe vers le meme handle vu que tu peux y avoir acces :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    function CreerBitmapLocal(pic: TImage): TBitmap;
    begin
      Result := TBitmap.Create;
      Result.Width := pic.Width;
      Result.Height := pic.Height;
      Result.Canvas.Handle := pic.Canvas.Handle;
    end;

  20. #20
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 430
    Points
    28 430
    Par défaut
    Citation Envoyé par KlausGunther Voir le message
    Cela plante avec violation d'accès sur la ligne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      Persist.SaveToSTream(Stream);
    Je pense que la raison en est toujours la même, celle que tu as décrite.
    ça avait des chances de fonctionner car TStream possède des méthodes virtuelles, c'est donc bien le code de la DLL qui est invoqué...mais encore faut-il avoir la même version du compilateur

    Citation Envoyé par KlausGunther Voir le message
    Qu'est-ce tu penses de ma tentative de décrypter la structure de l'objet TImage dont l'adresse est passée en paramètre ? As-tu des informations qui me manquent dans le tableau, et en particulier l'offset du pointeur vers la propriété TPicture du TImage, et celle du TBitmap du TPicture ? Car je peux accéder par un pointeur de type pinteger, à tous les offsets que je veux...
    c'est toujours la même histoire, si tu as la même version de Delphi, tu peux lui demander de le calculer pour toi: ofs := Integer(@Image1.Picture) - Integer(Image1)
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

Discussions similaires

  1. [D6] Comment modifier la taille du Canvas d'un TImage ?
    Par Lung dans le forum Composants VCL
    Réponses: 8
    Dernier message: 02/01/2015, 15h53
  2. Comment trouver son adresse IP locale ?
    Par theclem35 dans le forum Débuter
    Réponses: 5
    Dernier message: 07/06/2011, 19h55
  3. comment trouver l'adresse IP du serveur d'un site
    Par glloq8 dans le forum Général Conception Web
    Réponses: 3
    Dernier message: 27/01/2011, 14h55
  4. [OL-2000] Comment trouver la/les mauvaise(s) adresse(s)
    Par lediz dans le forum Outlook
    Réponses: 0
    Dernier message: 07/05/2010, 11h11

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo