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

Langage Delphi Discussion :

Comment "surcharger" une fonction "statique" ?


Sujet :

Langage Delphi

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut Comment "surcharger" une fonction "statique" ?
    Bonjour,

    J'aimerai dans mon appli Delphi pouvoir utiliser les thèmes Windows (pour avoir un look "moins rétro" ;-)) tout en conservant la possibilité de changer la couleur de police. En effet l'utilisation des thèmes dans une application rend impossible le changement de couleur du texte pour certains composants (TGroupBox, TButton, etc...).

    J'ai donc regardé rapidement l'implémentation de la VCL, il en ressort que ce sont principalement deux fonctions qui sont utilisées pour "dessiner le texte" lorsque le thème est actif :
    - "UxTheme.DrawThemeText"
    - "UxTheme.DrawThemeTextEx"


    La première est un pointeur définie lors de l'appel à "InitThemeLibrary", il est donc très facile de modifier ce pointeur une fois l'appel effectué pour le rediriger vers ma fonction qui modifie le comportement comme je le souhaite.

    J'ai par contre un problème avec "UxTheme.DrawThemeTextEx" qui est une fonction inline qui "appelle" la fonction correspondante de la librairie "uxtheme.dll".
    J'ignore d'ailleurs à quoi elle sert réellement puisqu'elle ne fait qu'appeler la même fonction de la librairie et possède le même nom. Seule la convention d'appel change (stdcall pour la fonction de la dll, fastcall pour la fonction inline "englobante").

    Comment puis-je modifier le comportement de cette fonction ?
    Comme il s'agit d'une fonction "statique" je ne peux évidemment pas faire "TDrawThemeTextEx( Addr(UxTheme.DrawThemeTextEx)^ ):= MyDrawThemeTextEx" car cela entraîne une violation d'accès.
    Puis-je "hooker" localement (sur le processus) cette fonction de la dll ? Je sais que cela peut se faire sur certaines fonctions de certaines dll mais j'ignore si dans mon cas cela est possible, et honnêtement je ne sais plus comment on fait.

    Merci pour votre aide.

  2. #2
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 612
    Points : 25 303
    Points
    25 303
    Par défaut
    C'est une encaspulation Delphi des API Windows : DrawThemeTextEx

    C'est une fonction "inline", tu as quel Delphi ?

    En 2007, il y a une variable _DrawThemeTextEx qui contient le pointeur vers la fonction de la DLL Windows et ce n'est pas inline

    Tu as donc un Delphi plus récent, si XE2, tu as un système de gestion de thème assez poussé pour donner un style général a ton application (autre que ceux du système)

    Tu peux tricher, recopie l'unité UxTheme dans ton projet, inclut là au projet, il utilisera cette unité au lieu de celle de Delphi
    Mais difficile de bosser sur HDC, je ne vois pas comment tu pourrais (sans une grosse uzinagaz) savoir si c'est un TGroupBox qui est dessiné ou un autre composant !

    Tu devrais plutôt faire tes propres classes hérités de TCustomGroupBox, TCustomButton ... et gérer faire les Paint

    Peut-être qu'en détournant la WndProc\WM_PAINT, tu pourrais redessiner par dessus avec toutes les difficultés de positionnement, tes clignotements ...

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    C'est une encaspulation Delphi des API Windows : DrawThemeTextEx

    C'est une fonction "inline", tu as quel Delphi ?

    En 2007, il y a une variable _DrawThemeTextEx qui contient le pointeur vers la fonction de la DLL Windows et ce n'est pas inline

    Tu as donc un Delphi plus récent, si XE2, tu as un système de gestion de thème assez poussé pour donner un style général a ton application (autre que ceux du système)

    Tu peux tricher, recopie l'unité UxTheme dans ton projet, inclut là au projet, il utilisera cette unité au lieu de celle de Delphi
    Mais difficile de bosser sur HDC, je ne vois pas comment tu pourrais (sans une grosse uzinagaz) savoir si c'est un TGroupBox qui est dessiné ou un autre composant !

    Tu devrais plutôt faire tes propres classes hérités de TCustomGroupBox, TCustomButton ... et gérer faire les Paint

    Peut-être qu'en détournant la WndProc\WM_PAINT, tu pourrais redessiner par dessus avec toutes les difficultés de positionnement, tes clignotements ...

    J'ai Delphi XE, et je compte justement passer à XE2 pour cette gestion des styles. J'ai pu voir un peu le fonctionnement. Il y a d'ailleurs un designeur de style pas très pratique à utiliser... le but est justement de pouvoir utiliser ces styles tout en gardant la possibilité de redéfinir la couleur de la police pour tous les composants (car comme je le disais certains d'entre eux sont récalcitrants à ce niveau).


    il est inenvisageable que je refasse des composants dédiés ou que je recopie l'unité "UxTheme" car cela demanderait respectivement trop de modifications de code (il s'agit d'une grosse application existante) et le résultat serait incertain pour l'unité "UxTheme" modifiée (comment m'assurer que les utilisent composants utilisent bien mon unité modifiée et pas l'unité de base ?).

    Dans Delphi XE la déclaration de la fonction "DrawThemeTextEx" est la suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function DrawThemeTextEx(hTheme: HTHEME; hdc: HDC; iPartId: Integer;
      iStateId: Integer; pszText: LPCWSTR; cchText: Integer; dwTextFlags: DWORD;
      pRect: PRect; var pOptions: TDTTOpts): HResult; stdcall; external themelib name 'DrawThemeTextEx' delayed;
     
    function DrawThemeTextEx(hTheme: HTHEME; hdc: HDC; iPartId: Integer;
      iStateId: Integer; pszText: UnicodeString; cchText: Integer; dwTextFlags: DWORD;
      var pRect: TRect; var pOptions: TDTTOpts): HResult;
    begin
      Result := DrawThemeTextEx(hTheme, hdc, iPartId, iStateId,
        PWideChar(pszText), cchText, dwTextFlags, @pRect, pOptions);
    end;
    Elle est donc assez curieuse puisqu'on a une liaison "statique décalée" qui s'apparente en fait plus à une liaison "dynamique", ainsi qu'une fonction qui pointe sur cette liaison "statique décalée". Sa seule utilité est visiblement de proposer une déclaration plus "Delphi-esque" (passage par référence plutôt que pointeur ou string plutôt que LPXXSTR)...


    Etant donné que la liaison de la première déclaration est en réalité dynamique, il devrait être possible de modifier le pointeur de la fonction sous-jacente, mais il faudrait déjà que j'arrive à récupérer le pointeur sous-jacent de cette fonction....

    En attendant j'ai trouvé un autre moyen en patchant directement la dll chargée en mémoire par le processus, je fais en sorte de rediriger l'ancienne fonction vers la mienne. C'est un peu violent car je modifie le code à la volée donc obligé d'autoriser un accès en écriture à la zone mémoire et de vider le cache d'instruction, mais au moins cela fonctionne. Le seul hic c'est que pour appeler la méthode originale je suis obligé de désactiver le patch pour le réactivé après, donc pénalité d'un point de vue perf... j'espère d'ailleurs que cela ne risque pas d'être vu comme du code "dangereux" par certains antivirus...

  4. #4
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Citation Envoyé par ZZZzzz2 Voir le message
    J'ai Delphi XE, et je compte justement passer à XE2 pour cette gestion des styles. J'ai pu voir un peu le fonctionnement. Il y a d'ailleurs un designeur de style pas très pratique à utiliser... le but est justement de pouvoir utiliser ces styles tout en gardant la possibilité de redéfinir la couleur de la police pour tous les composants (car comme je le disais certains d'entre eux sont récalcitrants à ce niveau).


    il est inenvisageable que je refasse des composants dédiés ou que je recopie l'unité "UxTheme" car cela demanderait respectivement trop de modifications de code (il s'agit d'une grosse application existante) et le résultat serait incertain pour l'unité "UxTheme" modifiée (comment m'assurer que les utilisent composants utilisent bien mon unité modifiée et pas l'unité de base ?).

    Dans Delphi XE la déclaration de la fonction "DrawThemeTextEx" est la suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function DrawThemeTextEx(hTheme: HTHEME; hdc: HDC; iPartId: Integer;
      iStateId: Integer; pszText: LPCWSTR; cchText: Integer; dwTextFlags: DWORD;
      pRect: PRect; var pOptions: TDTTOpts): HResult; stdcall; external themelib name 'DrawThemeTextEx' delayed;
     
    function DrawThemeTextEx(hTheme: HTHEME; hdc: HDC; iPartId: Integer;
      iStateId: Integer; pszText: UnicodeString; cchText: Integer; dwTextFlags: DWORD;
      var pRect: TRect; var pOptions: TDTTOpts): HResult;
    begin
      Result := DrawThemeTextEx(hTheme, hdc, iPartId, iStateId,
        PWideChar(pszText), cchText, dwTextFlags, @pRect, pOptions);
    end;
    Elle est donc assez curieuse puisqu'on a une liaison "statique décalée" qui s'apparente en fait plus à une liaison "dynamique", ainsi qu'une fonction qui pointe sur cette liaison "statique décalée". Sa seule utilité est visiblement de proposer une déclaration plus "Delphi-esque" (passage par référence plutôt que pointeur ou string plutôt que LPXXSTR)...


    Etant donné que la liaison de la première déclaration est en réalité dynamique, il devrait être possible de modifier le pointeur de la fonction sous-jacente, mais il faudrait déjà que j'arrive à récupérer le pointeur sous-jacent de cette fonction....

    En attendant j'ai trouvé un autre moyen en patchant directement la dll chargée en mémoire par le processus, je fais en sorte de rediriger l'ancienne fonction vers la mienne. C'est un peu violent car je modifie le code à la volée donc obligé d'autoriser un accès en écriture à la zone mémoire et de vider le cache d'instruction, mais au moins cela fonctionne. Le seul hic c'est que pour appeler la méthode originale je suis obligé de désactiver le patch pour le réactivé après, donc pénalité d'un point de vue perf... j'espère d'ailleurs que cela ne risque pas d'être vu comme du code "dangereux" par certains antivirus...

    Bon ma méthode ne me donne pas satisfaction pour plusieurs raisons :

    1/ j'ai parfois des violations d'accès au design depuis... que je peux éviter en n'activant le patch qu'au runtime par code, mais cela ne devrait pas être utile et du coup au design je ne vois pas les changements de couleur de texte.

    2/ L'API WindowFromDC me retourne parfois des handles de fenêtres contenues par un process externes (notament csrss.exe...), je ne sais pas trop pourquoi. Cela ne concerne que certains composants comme le TLabel mais du coup m'empêche de récupérer le "tcontrol" sous-jacent à "HDC" passé en argument à la fonction "DrawThemeText" ou "DrawThemeTextEx"... Donc je suis un peu coincé avec tout ça.....

    Si quelqu'un a une idée ;-)...


    3/ Concrètement je ne trouve pas le code correspond au "dessin" du "TButton", je soupsçonne que l'implémentation est interne à Windows et que le TButton encapsule donc un contrôle interne de l'OS. Du coup je pense pour ce cas me rabattre sur le "TBitBtn" qui lui permet de changer la couleur du texte (vous avez dit bizarre ;-) ?), je remplacerai toutes les occurrences dans le code, ça, ça peut se faire facilement, et ça reste un compo. VCL de base.

  5. #5
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 612
    Points : 25 303
    Points
    25 303
    Par défaut
    Citation Envoyé par ZZZzzz2 Voir le message
    3/ Concrètement je ne trouve pas le code correspond au "dessin" du "TButton", je soupsçonne que l'implémentation est interne à Windows et que le TButton encapsule donc un contrôle interne de l'OS.
    Fichtre !
    Tu soupçonne ce qui est écrit dans la documentation : "TWinControl est la classe de base de tous les contrôles qui sont des enveloppes pour les objets d'écran Windows."

    Comme la TListBox, TListView, TMemo, TTreeNode, TComboBox ... ben tous les TWinControl en fait !

    WM_PAINT étant là pour ajouter son dessin personnalisé (si on y arrive)
    Cela sans compter les messages spécifiques à chaque contrôle ou les modes manuels et tous les effets de bords que cela pose de gérer le dessin d'un TWinControl à la main !

    Les TWinControl sont très différents des TGraphicControl comme TLabel ou TSpeedButton

    Citation Envoyé par ZZZzzz2 Voir le message
    Du coup je pense pour ce cas me rabattre sur le "TBitBtn" qui lui permet de changer la couleur du texte (vous avez dit bizarre ;-) ?),
    Celui là c'est vraiment un cas étrange, il a été conçu sur un TWinControl mais codé comme TGraphicControl car utilise le Style BS_OWNERDRAW
    Et l'on évoquait justement dans le sujet [XE2] Vcl.themes et PageControl.OnDrawTab l'importance du CN_DRAWITEM

    Voir aussi RegisterStyleHook ou tu dois pouvoir définir un nouveau style pour une classe donnée comme TBitBtn et TButton, le Paint, PaintBackground et PaintNC

    Rappelons que TButton hérite de TCustomButton et que la documentation de RegisterStyleHook fourni cet extrait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TCustomStyleEngine.RegisterStyleHook(TCustomButton,TButtonStyleHook);
    TButtonStyleHook

    En faisant ton propre TButtonStyleHook qui lui utiliserait les propriétés TControl.Color, Font.Color ... faudra bidouiller pour Color qui n'est pas publié donc faudra l'affecter manuellement !

    Il n'ont pas pris ce cas par hasard !

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Fichtre !
    Tu soupçonne ce qui est écrit dans la documentation : "TWinControl est la classe de base de tous les contrôles qui sont des enveloppes pour les objets d'écran Windows."

    Comme la TListBox, TListView, TMemo, TTreeNode, TComboBox ... ben tous les TWinControl en fait !

    WM_PAINT étant là pour ajouter son dessin personnalisé (si on y arrive)
    Cela sans compter les messages spécifiques à chaque contrôle ou les modes manuels et tous les effets de bords que cela pose de gérer le dessin d'un TWinControl à la main !

    Les TWinControl sont très différents des TGraphicControl comme TLabel ou TSpeedButton



    Celui là c'est vraiment un cas étrange, il a été conçu sur un TWinControl mais codé comme TGraphicControl car utilise le Style BS_OWNERDRAW
    Et l'on évoquait justement dans le sujet [XE2] Vcl.themes et PageControl.OnDrawTab l'importance du CN_DRAWITEM

    Voir aussi RegisterStyleHook ou tu dois pouvoir définir un nouveau style pour une classe donnée comme TBitBtn et TButton, le Paint, PaintBackground et PaintNC

    Rappelons que TButton hérite de TCustomButton et que la documentation de RegisterStyleHook fourni cet extrait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TCustomStyleEngine.RegisterStyleHook(TCustomButton,TButtonStyleHook);
    TButtonStyleHook

    En faisant ton propre TButtonStyleHook qui lui utiliserait les propriétés TControl.Color, Font.Color ... faudra bidouiller pour Color qui n'est pas publié donc faudra l'affecter manuellement !

    Il n'ont pas pris ce cas par hasard !
    Je te remercie pour ta réponse.

    "TBitBtn" et "TButton" héritant tout deux de "TCustomButton" qui hérite lui-même de "TButtonControl" héritant de "TWinControl", il n'est pas forcément trivial de comprendre pourquoi on a le code de l'un et pas de l'autre en ce qui concerne le dessin mais je savais qu'un certain nombre de composants Delphi étaient des contrôles système (Windows) sous-jacents et donc qu'on n'a malheureusement pas accès à toute leur implémentation d'une certaines manière.


    Sinon depuis je viens donc de passer à XE2 et pas mal de choses ont changé entre temps avec l'ajout des styles.

    J'ai écris un patch pour la méthode "TStyleHook.DrawControlText" qui marche bien (sans entrer dans les détails j'ai utilisé le RTTI combiné à la méthode "bourraine" de patch vu précédement pour cela) :

    Chose drôle, lorsque je suis en "look système", le "TButton" ne supporte pas la modification de couleur de texte mais le "TBitBtn" le supporte...
    En look "style personnalisé", grâce à mon patch, c'est l'inverse car "TButton" appelle alors en interne "TStyleHook.DrawControlText" tandis que TBitBtnqui fait "sa sauce" avec "Vcl.Buttons.TButtonGlyph.DrawButtonText" utilise systématiquement la couleur du style en priorité.

    Comme "TButtonGlyph" est une classe cachée dans l'implémentation de l'unité "Vcl.Buttons", je ne vois aucun moyen de la patcher (le RTTI n'y accède pas). Je suis donc coincé là dessus.


    Je crois que la bonne approche est donc soit de ne pas spécifier de couleur de police dans le style (="clNone")ou d'avoir de nouvelles propriétés pour la police (fswarning, fserror, fsinformative, etc.)

    Ou alors je galère à mort en modifiant (quand c'est possible) les différents "StyleHook"...

Discussions similaires

  1. Comment savoir quand créer une fonction/procédure
    Par aurelien demarest dans le forum Débuter
    Réponses: 5
    Dernier message: 12/11/2011, 21h39
  2. Réponses: 1
    Dernier message: 11/06/2010, 18h23
  3. comment passer un tableau à une fonction javascript
    Par beegees dans le forum Général JavaScript
    Réponses: 3
    Dernier message: 08/03/2009, 19h48
  4. comment conecter un bouton à une fonction?
    Par Krishna dans le forum GTK+ avec C & C++
    Réponses: 6
    Dernier message: 25/03/2008, 21h28
  5. Comment creer et appeler une fonction sql..
    Par denissay dans le forum Langage SQL
    Réponses: 7
    Dernier message: 05/01/2008, 22h28

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