none
Utilisation SB_GETTEXT (lecture du texte dans une barre de statut) RRS feed

  • Question

  • Bonjour,

    Je cherche à lire le texte d'un élément d'une barre de statut, mais je n'y arrive pas.

    Par exemple, supposons que je cherche à récupérer le texte du 1er élément de la barre de statut de Wordpad. Si j'utilise WM_GETTEXT, cela marche parfaitement. Mais dès que je veux utiliser SB_GETTEXT, j'obtiens un plantage systématique.

    Le code VBA que j'utilise est donné ci-dessous (il est directement utilisable dans Excel par copier/coller - avec Wordpad ouvert en parallèle bien sûr !). Quelqu'un saurait il me dire d'où vient le problème ? Merci d'avance pour votre aide, parce que là, je sèche...

    Simon

    'API et constantes
    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
    Private Declare Function GetDlgItem Lib "user32.dll" (ByVal hDlg As Long, ByVal nIDDlgItem As Long) As Long
    Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByRef lParam As Any) As Long
    Private Const WM_GETTEXT As Long = &HD
    Private Const SB_GETTEXT As Long = &H402
    Private Const SB_GETTEXTLENGTH = &H403

    Sub test1_WM_GETTEXT()

    'Handles de la fenêtre et de la barre de statut
    Dim hWordpadWnd As Long
    Dim hWordpadStatusBar As Long
    hWordpadWnd = FindWindow("WordPadClass", vbNullString)
    hWordpadStatusBar = GetDlgItem(hWordpadWnd, 59393)
    MsgBox "Handle de la fenêtre: " & hWordpadWnd & " | Handle de la barre de statut: " & hWordpadStatusBar

    'Lecture du texte de la barre de statut
    Dim sbuffer As String
    sbuffer = Space$(50)
    SendMessage hWordpadStatusBar, WM_GETTEXT, 50, ByVal sbuffer
    MsgBox "Texte de la barre de statut: " & sbuffer

    End Sub


    Sub test2_SB_GETTEXT()

    'Handles de la fenêtre et de la barre de statut
    Dim hWordpadWnd As Long
    Dim hWordpadStatusBar As Long
    hWordpadWnd = FindWindow("WordPadClass", vbNullString)
    hWordpadStatusBar = GetDlgItem(hWordpadWnd, 59393)
    MsgBox "Handle de la fenêtre: " & hWordpadWnd & " | Handle de la barre de statut: " & hWordpadStatusBar

    'Lecture du texte de la barre de statut
    Dim String_size As Long
    Dim sbuffer As String
    'Taille de la chaine
    String_size = SendMessage(hWordpadStatusBar, SB_GETTEXTLENGTH, ByVal 0, ByVal 0)
    String_size = (String_size And &HFFFF&)
    MsgBox "Nombre de caractères lus: " & String_size
    'Récupération du texte
    sbuffer = Space$(String_size)
    SendMessage hWordpadStatusBar, SB_GETTEXT, ByVal 0, ByVal sbuffer
    MsgBox "Texte de la barre de statut: " & sbuffer

    End Sub

    vendredi 30 juillet 2010 17:42

Réponses

  • Hello,

    Le problème est très subtil. Tu ne peux faire comme cela que pour lire le contenu d'une status bas de TON application, pas d'une application externe. la raison est que le buffer transmis au Send message avec SB_GETTEXT doit être alloué dans un process externe à l'application. Donc, pas d'allocation VB avec Space$() ou String$().

     

    La solution est d'utiliser VirtualAlloc() [pas très simple à faire] ou de faire ton allocation via une petite Dll en C (le plus commode, amha).

    Ce problème est évoqué ici: http://www.mofeel.net/950-microsoft-public-vb-winapi/5997.aspx

    Sinon, pour faire une Dll en C et l'interfacer avec VB, voir par exempple la FAQ : http://faq.vb.free.fr/index.php?question=144

    Ceci dit, tu peux souvent te passer en pratique de SB_GETTEXT, en utilisant WM_GETTEXT comme tu le fais dans test1_WM_GETTEXT();

     

    Cordialement,


     

     


    Jean-marc
    • Marqué comme réponse Chouchensb dimanche 1 août 2010 20:01
    samedi 31 juillet 2010 18:02

Toutes les réponses

  • Hello,

    Le problème est très subtil. Tu ne peux faire comme cela que pour lire le contenu d'une status bas de TON application, pas d'une application externe. la raison est que le buffer transmis au Send message avec SB_GETTEXT doit être alloué dans un process externe à l'application. Donc, pas d'allocation VB avec Space$() ou String$().

     

    La solution est d'utiliser VirtualAlloc() [pas très simple à faire] ou de faire ton allocation via une petite Dll en C (le plus commode, amha).

    Ce problème est évoqué ici: http://www.mofeel.net/950-microsoft-public-vb-winapi/5997.aspx

    Sinon, pour faire une Dll en C et l'interfacer avec VB, voir par exempple la FAQ : http://faq.vb.free.fr/index.php?question=144

    Ceci dit, tu peux souvent te passer en pratique de SB_GETTEXT, en utilisant WM_GETTEXT comme tu le fais dans test1_WM_GETTEXT();

     

    Cordialement,


     

     


    Jean-marc
    • Marqué comme réponse Chouchensb dimanche 1 août 2010 20:01
    samedi 31 juillet 2010 18:02
  • Merci beaucoup Jean-Marc ! Ton aide m'a été plus que précieuse, je n'aurai jamais trouvé ça tout seul !

    Du coup, en faisant des recherches sur VirtualAlloc, je suis tombé sur un code VBA similaire et je l'ai adapté:

    http://www.vbfrance.com/codes/EXTRAIRE-TEXTE-FENETRES-APPLICATIONS-TIERCES-LISTBOX-LISTVIEW-TEXTBOXES_46390.aspx

    Le code obtenu est donné ci-dessous et fonctionne à merveille ! Je ne saisi pas encore clairement le pourquoi des API utilisées (n'étant pas vraiment informaticien), mais je vais m'y pencher. A ce sujet, aurais tu un site web ou même un livre à conseiller pour comprendre tout cela ?

    Et dernière question (si je n'abuse pas trop): d'où vient ce problème d'allocation mémoire et est-il uniquement présent sur les éléments string ?

    Merci encore pour ton aide.

    Simon

     

    'API et constantes
    Private Declare Function OpenProcess Lib "kernel32.dll" (ByVal dwDesiredAccessas As Long, ByVal bInheritHandle As Long, ByVal dwProcId As Long) As Long
    Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, lpdwProcessId As Long) As Long
    Private Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
    Private Declare Function GetDlgItem Lib "user32.dll" (ByVal hDlg As Long, ByVal nIDDlgItem As Long) As Long
    Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByRef lParam As Any) As Long
    Private Declare Function VirtualAllocEx Lib "kernel32" (ByVal hProcess As Long, ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
    Private Declare Function VirtualFreeEx Lib "kernel32" (ByVal hProcess As Long, lpAddress As Any, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long
    Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, ByVal lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long

    Private Const STANDARD_RIGHTS_REQUIRED As Long = &HF0000
    Private Const SYNCHRONIZE As Long = &H100000
    Private Const PROCESS_ALL_ACCESS As Long = (STANDARD_RIGHTS_REQUIRED Or SYNCHRONIZE Or &HFFF&)

    Private Const MEM_RELEASE = &H8000
    Private Const MEM_COMMIT = &H1000
    Private Const PAGE_READWRITE = &H4

    Private Const WM_GETTEXT = &HD
    Private Const SB_GETTEXT = &H402
    Private Const SB_GETTEXTLENGTH = &H403

    Sub test2_SB_GETTEXT()

        Dim StatusBar_Item_Nb
        StatusBar_Item_Nb = 0   'Numéro de l'élément à traiter dans la barre de statut
       
        Dim pid As Long
        Dim process As Long
        Dim ptr_sBuffer As Long
        Dim sBuffer As String
        Dim sBufferSize As Long
           
        '~~ Récupération des handlers de la fenêtre et de la barre de statut ~~
        Dim hWordpadWnd As Long
        Dim hWordpadStatusBar As Long
        hWordpadWnd = FindWindow("WordPadClass", vbNullString)
        hWordpadStatusBar = GetDlgItem(hWordpadWnd, 59393)
       
        '~~ Récupération de la taille de la chaine de caractère avec SB_GETTEXTLENTGH~~
        sBufferSize = SendMessage(hWordpadStatusBar, SB_GETTEXTLENGTH, StatusBar_Item_Nb, 0)
        sBufferSize = (sBufferSize And &HFFFF&)
        sBuffer = Space$(sBufferSize)
       
        '~~ Récupération du contenu de la chaine de caratère ~~
        'On récupère le process ID de la barre de statut
        GetWindowThreadProcessId hWordpadStatusBar, pid
        'A partir de ce process ID, on récupère le handler du process...
        process = OpenProcess(PROCESS_ALL_ACCESS, False, pid)
        '... et on alloue un emplacement mémoire au sein de ce process
        ptr_sBuffer = VirtualAllocEx(process, 0, sBufferSize, MEM_COMMIT, PAGE_READWRITE)
        'On copie ensuite le texte dans l'emplacement mémoire à l'aide de SB_GETTEXT...
        SendMessage hWordpadStatusBar, SB_GETTEXT, StatusBar_Item_Nb, ByVal ptr_sBuffer
        '...et on vient lire cet emplacement mémoire pour récupèrer le texte
        ReadProcessMemory process, ByVal ptr_sBuffer, ByVal sBuffer, sBufferSize, 0
        'Pour finir, on libère l'emplacement mémoire alloué
        VirtualFreeEx process, ptr_sBuffer, 0, MEM_RELEASE

        MsgBox "Item length: " & Len(sBuffer)
        MsgBox "Item: " & sBuffer

    End Sub

     

    dimanche 1 août 2010 20:01
  • Hello,

    Les API utilisées (VirtualAlloc et consorts) sont des API de gestion mémoire de bas niveau, permettant d'allouer de la mémoire dans l'adress space d'une application donnée (Ca permet d'autres choses encore, tel que mémoire partagée, etc.). Je ne connais pas la raison interne qui oblige à utiliser ces API pour l'utilisation de SB_GETTEXT, je sais juste qu'il faut faire comme ça.

    Concernant les références, mon livre favori sur le sujet est celui du célébrissime Dan Appleman, mais il y en a d'autres. Voir cett page par exemple: http://visualbasic.about.com/od/usevb6/l/aa103002a.htm

    Pour ce qui concerne ta dernière question, c'est difficile d'y répondre. E fait, il n'y a pas à proprement parler de problème d'allocation. Le fait est que pour certains messages, il y a une restriction sur le type de mémoire à allouer.

    Une petite remarque en passant: la ou tu écris:

    sBuffer = Space$(sBufferSize)

    Je remplacerais par 

    sBuffer = Space$(sBufferSize + 1)

    En effet,, ces API retournent des chaines au sens "C", c'est à dire terminées par zero. En pratique, pour stocker "TOTO" (4 caracteres) il faut 5 octets : "T" "O" "T" "O" "0x00".

    Ca me semble donc plus raisonnable d'allouer un octet de plus, ça ne mange pas de pain de toute façon :-)

     

    Cordialement,


     


    Jean-marc
    lundi 2 août 2010 07:32
  • Merci beaucoup pour ces informations additionelles. Du coup, je viens de commander le Dan Appleman, je pense que ça m'éclairera un peu plus sur l'utilisation de toutes ces API incompréhensibles (bouquin d'occasion, apparemment il est plus édité, mais au moins c'est moins cher) !

    Simon

    lundi 2 août 2010 21:26