Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Lire une zone de texte par API

13 réponses
Avatar
Gloops
Bonjour tout le monde,

Je cherche à copier le contenu d'une zone de texte d'une application
extérieure, par exemple vers un fichier texte.

Pour cela, il m'a semblé que SendMessage pouvait être un bon filon,
j'espère ne pas m'être trompé. Alors je commence par faire des exercices.

Je me suis référé à ce qui est fourni avec VB6, à savoir une copie de la
fiche 141073. Pour la trouver dans HTML de VB6, on peut y chercher par
exemple EM_GETLINE, et trouver dans les rubriques "Using Windows API
Functions to Better Manipulate Text Boxes".

Il faut mettre les déclarations au goût du jour :
Private Declare Function GetFocus Lib "user32" () As Long
Private Declare Function SendMessage Lib "user32" _
Alias "SendMessageA" _
(ByVal hWnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
lParam As Any) As Long
Private Const EM_GETLINECOUNT = &HBA
Private Const EM_GETLINE = &HC4
Private Const EM_LINELENGTH = &HC1
Private Const EM_GETSEL = &HB0
Private Const EM_LINEFROMCHAR = &HC9
Private Const EM_LINEINDEX = &HBB
Private Const EM_SETSEL = &HB1
Private Const EM_REPLACESEL = &HC2

J'ai désactivé la deuxième instruction de Form_Load,
X% = fReplaceSel("")
le motif en est indiqué par le commentaire.

ça démarrait bien, et puis l'application s'est plantée, et VB6 avec.

J'ai tout remis en place, et il s'avère que le plantage se produit dans
fGetLine, au niveau de SendMessage

hWnd = GetFocus()
Pos& = SendMessage(hWnd, EM_GETLINE, CInt(LineNumber), Buffer)

Un peu plus haut, ça n'a pas suffi d'agrandir un peu le buffer, de 2
octets, ni ensuite de le doubler :
Buffer = Chr$(byteLo%) + Chr$(byteHi%) _
+ Space$(2 * MAX_CHAR_PER_LINE) ' exit - 2)

- 2 ou pas - 2, doublé ou pas doublé la taille du buffer, même motif
même punition, VB manque de mémoire, alors il se suicide.

Qui saura le dissuader de franchir le parapet ?

10 réponses

1 2
Avatar
Gloops
Ah oui histoire d'être clair, manque de mémoire, ça s'appelle erreur 7.

Gloops a écrit, le 22/06/2005 02:45 :

- 2 ou pas - 2, doublé ou pas doublé la taille du buffer, même motif
même punition, VB manque de mémoire, alors il se suicide.

Qui saura le dissuader de franchir le parapet ?



Avatar
Zoury
Salut Gloops ! :O)

Si tu déclare LParam "Byref .. As Any", tu dois employé ByVal lorsque tu
passes le buffer à SendMessage.

Ex :
'***
Option Explicit

Private Declare Function SendMessage _
Lib "user32" _
Alias "SendMessageA" ( _
ByVal hWnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByRef lParam As Any) As Long

Private Const EM_GETLINE = &HC4
Private Const EM_LINELENGTH = &HC1

Private Sub Form_Load()

Text1.Text = "Text1" & vbNewLine & "Text2" & vbNewLine & "Text3"

End Sub

Private Sub Command1_Click()

Call MsgBox(GetLine(Text1.hWnd, 1))

End Sub

Private Function GetLine(ByVal hWnd As Long, ByVal nLineNumber As Long) As
String

GetLine = Space$(SendMessage(hWnd, EM_LINELENGTH, nLineNumber, ByVal 0))
Call SendMessage(hWnd, EM_GETLINE, nLineNumber, ByVal GetLine)

End Function
'***

--
Cordialement
Yanick
MVP pour Visual Basic
Avatar
Gloops
Ah, quand il y a ByRef dans la déclaration, il faut employer ByVal dans
l'appel ...

Ben mon gars, heureusement que tu es là pour me le dire.

ça doit pourtant être écrit quelque part dans le Platinium, mais
apparemment à une page que je n'ai pas lue.

Merci, je vais essayer ça ce soir.
____________________________________
Zoury a écrit, le 22/06/2005 17:43 :
Salut Gloops ! :O)

Si tu déclare LParam "Byref .. As Any", tu dois employé ByVal lorsque tu
passes le buffer à SendMessage.

Ex :
'***
Option Explicit

Private Declare Function SendMessage _
Lib "user32" _
Alias "SendMessageA" ( _
ByVal hWnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByRef lParam As Any) As Long

Private Const EM_GETLINE = &HC4
Private Const EM_LINELENGTH = &HC1

Private Sub Form_Load()

Text1.Text = "Text1" & vbNewLine & "Text2" & vbNewLine & "Text3"

End Sub

Private Sub Command1_Click()

Call MsgBox(GetLine(Text1.hWnd, 1))

End Sub

Private Function GetLine(ByVal hWnd As Long, ByVal nLineNumber As Long) As
String

GetLine = Space$(SendMessage(hWnd, EM_LINELENGTH, nLineNumber, ByVal 0))
Call SendMessage(hWnd, EM_GETLINE, nLineNumber, ByVal GetLine)

End Function
'***



Avatar
Picalausa François
Hello,

En fait, il faut savoir que tu veux passer un pointeur vers une chaine
(Byval) et pas un pointeur vers un pointeur vers une chaine (Byref).
Tout cela est expliqué plus en détail dans le livre blanc à ce sujet:
http://www.microsoft.com/downloads/details.aspx?familyidÎ7DA635-78A1-457E-9959-C3996CF25C03&displaylang=fr

--
Picalausa François
"Gloops" a écrit dans le message de news:
42b9a59f$0$1237$
Ah, quand il y a ByRef dans la déclaration, il faut employer ByVal dans
l'appel ...

Ben mon gars, heureusement que tu es là pour me le dire.

ça doit pourtant être écrit quelque part dans le Platinium, mais
apparemment à une page que je n'ai pas lue.

Merci, je vais essayer ça ce soir.
____________________________________
Zoury a écrit, le 22/06/2005 17:43 :
Salut Gloops ! :O)

Si tu déclare LParam "Byref .. As Any", tu dois employé ByVal lorsque tu
passes le buffer à SendMessage.

Ex :
'***
Option Explicit

Private Declare Function SendMessage _
Lib "user32" _
Alias "SendMessageA" ( _
ByVal hWnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByRef lParam As Any) As Long

Private Const EM_GETLINE = &HC4
Private Const EM_LINELENGTH = &HC1

Private Sub Form_Load()

Text1.Text = "Text1" & vbNewLine & "Text2" & vbNewLine & "Text3"

End Sub

Private Sub Command1_Click()

Call MsgBox(GetLine(Text1.hWnd, 1))

End Sub

Private Function GetLine(ByVal hWnd As Long, ByVal nLineNumber As Long)
As
String

GetLine = Space$(SendMessage(hWnd, EM_LINELENGTH, nLineNumber, ByVal
0))
Call SendMessage(hWnd, EM_GETLINE, nLineNumber, ByVal GetLine)

End Function
'***






Avatar
Gloops
Ah pardon, j'ai compris depuis, c'est "As Any" qui implique de passer le
paramètre "By Val", un peu comme si, quand on promettait de raser
gratis, ça ne valait que si on précisait dans quelle monnaie, vu que
pour l'API la déclaration ne dit pas quel type de donnée la fonction est
en mesure de modifier, donc si on lui passe autre chose que ce avec quoi
elle a été conçue pour travailler, c'est logique, ça coince.

Je dois dire que quand je suis rentré cet après-midi je n'étais pas
frais, c'est peu de le dire.

ça fait que maintenant, tout marche, y compris l'affichage du texte de
la ligne, merci.

La fiche met la taille du tampon dans les deux premiers octets, comme on
fait en C si je me rappelle bien. Ce n'est pas précisé par la doc,
est-ce juste une habitude, ou est-ce qu'on risque aussi des ennuis si on
ne le fait pas ?

En attaquant ça je me suis dit que j'allais pouvoir recueillir les
données reçues par minitel avec l'hyperterminal, mais ça n'est peut-être
pas aussi simple que ça. On n'est pas guidé par les titres des
contrôles. Si j'en juge par le numéro retourné par WindowFromPoint avec
le curseur sur la zone visée, on devrait pouvoir attaquer le dernier
enfant de la fenêtre principale de l'hyperterminal (sur quatre si je me
rappelle bien, les autres étant organisés en hiérarchie sur ces quatre),
mais SendMessage WM_GETTEXT ne retourne rien là-dessus, pas plus que
EM_GETLINECOUNT, EM_GETHANDLE, EM_LINELENGTH (après avoir bien sûr pris
soin de recevoir des données). Ou alors il y a plusieurs fenêtres
superposées, et WindowFromPoint ne peut retourner qu'un numéro. Il va
peut-être falloir que j'affiche toute la hiérarchie, avec les
coordonnées et quand c'est possible le texte ...

Ou alors ce n'est pas une zone de texte ? C'est vrai qu'il y a des gras
et des italiques, à afficher ... Il faudra que je regarde si on a des
messages prévus pour une RichText, avec SendMessage.

Bon, avant tout je vais lire la doc proposée par François ...
Avatar
Gloops
Bonjour,

Ah ben au moins je n'ai pas posé ma question pour rien.
Je vais regarder ça tranquillement demain, merci.
_________________________________________________
Picalausa François a écrit, le 22/06/2005 20:03 :

Hello,

En fait, il faut savoir que tu veux passer un pointeur vers une chaine
(Byval) et pas un pointeur vers un pointeur vers une chaine (Byref).
Tout cela est expliqué plus en détail dans le livre blanc à ce sujet:
http://www.microsoft.com/downloads/details.aspx?familyidÎ7DA635-78A1-457E-9959-C3996CF25C03&displaylang=fr



Avatar
Gloops
Picalausa François a écrit, le 22/06/2005 20:03 :
http://www.microsoft.com/downloads/details.aspx?familyidÎ7DA635-78A1-457E-9959-C3996CF25C03&displaylang=fr



Bonjour,

Intéressant. Je crois que j'ai loupé les shémas en lisant ça dans Word
95, en rentrant à la maison je chercherai la visionneuse.

J'ai repris les exemples des pages 8 et 9, et je n'ai pas eu exactement
les mêmes résultats, peut-être quelque chose m'a-t-il échappé.

================= Début ===================== Private Declare Sub CopyMemory Lib "kernel32" _
Alias "RtlMoveMemory" _
(pDst As Any, pSrc As Any, ByVal ByteLen As Long)

Dim maChaine As String

Public Sub maProc()
Dim monTab(19) As Byte
Dim i As Integer

For i = 0 To 18: monTab(i) = 0: Next

maChaine = "AIDE"


CopyMemory monTab(0), ByVal maChaine, LenB(maChaine)


Debug.Print "Len(maChaine) = " & Len(maChaine)

For i = 0 To 19
Debug.Print i, monTab(i), Chr$(monTab(i))
Next

End Sub
================= Fin =======================
Résultat :
Form1.maProc
Len(maChaine) = 4
0 65 A
1 73 I
2 68 D
3 69 E
4 0
5 0
6 106 j
7 0
8 0
et le reste à 0.

J'ai un caractère aléatoire en position 6, si je fais plusieurs appels
successifs ce caractère ne sera pas forcément le même. Les quatre
premiers caractères, en revanche, sont bien ceux qu'on a voulu passer.

Si maintenant, en longueur, au lieu de LenB(maChaine), je mets
len(maChaine), je passe juste les caractères, et j'obtiens

0 65 A
1 73 I
2 68 D
3 69 E
4 0
5 0
6 0
7 0
8 0

donc, on a toujours ce qu'il faut, mais cette fois juste ce qu'il faut.

C'est quoi que j'ai compris de travers ?

Avec l'exemple suivant j'ai aussi un petit souci de compréhension.

Dans le même module j'ajoute :
================= Début ===================== Sub maProc2()
Dim lg As Long
Dim monTab(1 To 20) As Byte
'Dim i As Integer

maChaine = "AIDE"
lg = StrPtr(maChaine)
CopyMemory monTab(1), lg, LenB(maChaine)

For i = 1 To 20
Debug.Print i, monTab(i), Chr$(monTab(i))
Next

End Sub
================= Fin =======================
Et à l'appel j'obtiens :
Form1.maProc2
1 84 T
2 53 5
3 28 
4 0
5 252 ü
6 6 
7 0
8 0

et le reste à 0.
Je me gratte la tête, et je ne lis pas "AIDE".

Si en revanche je corrige ainsi :
CopyMemory monTab(1), ByVal lg, LenB(maChaine)

j'obtiens
Form1.maProc2
1 65 A
2 0
3 73 I
4 0
5 68 D
6 0
7 69 E
8 0
9 0
10 0
et le reste à 0.

J'avoue que je trouve plus lisible cette dernière version.
D'ailleurs, si j'ai bien suivi (parfois ça arrive) c'est bien la
correction qui m'a été proposée en réponse à mon premier message, pour
éviter que VB plante.

Je préfère vérifier que je n'ai pas tout compris de travers, en
corrigeant une erreur par une autre ...
Avatar
Gloops
Si j'ai bien lu le document proposé par François, les chaînes de
caractères (ah pardon, les BSTR) sont systématiquement passées en valeur
(du pointeur).
Donc, si il y a "As String", devant on mettra "ByVal". Enfin si ça
commence à se mettre un peu en place dans ma petite tête ...
Avatar
Picalausa François
Hello,

<réponse inline>

"Gloops" a écrit dans le message de news:
42ba7ee8$0$25021$
Picalausa François a écrit, le 22/06/2005 20:03 :
http://www.microsoft.com/downloads/details.aspx?familyidÎ7DA635-78A1-457E-9959-C3996CF25C03&displaylang=fr


J'ai repris les exemples des pages 8 et 9

J'ai un caractère aléatoire en position 6, si je fais plusieurs appels
successifs ce caractère ne sera pas forcément le même. Les quatre premiers
caractères, en revanche, sont bien ceux qu'on a voulu passer.



LenB("AIDE") renvoie 8
donc, on va copier 8 octets d'une chaine ANSI (1 octet/caractère) d'une
chaine qui n'en comporte que 4... il y aura donc des données non réservées à
la chaine après le 5ème (caractère nul de fin de chaine). De ce fait, elles
peuvent contenir les données de n'importe quelle autre variable.
La lecture n'est peut-être (quoi que?) pas bien méchant, mais maintenant,
supposons qu'au lieu de lire ces caractères non initialisé, on écrive des
caractères dans un espace qui ne nous est pas réservé... Et paf! c'est le
buffer overrun.
Cela explique que ça fonctionne avec Len.

Avec l'exemple suivant j'ai aussi un petit souci de compréhension.
J'avoue que je trouve plus lisible cette dernière version.
D'ailleurs, si j'ai bien suivi (parfois ça arrive) c'est bien la
correction qui m'a été proposée en réponse à mon premier message, pour
éviter que VB plante.



Positivement positif... l'exemple est erroné et passe un pointeur vers un
pointeur au lieu de passer un pointeur vers le début de la chaine.

--
Picalausa François
Avatar
Zoury
> Donc, si il y a "As String", devant on mettra "ByVal".



Exact !

Enfin si ça
commence à se mettre un peu en place dans ma petite tête ...



Ça m'a l'air d'être le cas. :O)


--
Cordialement
Yanick
MVP pour Visual Basic
1 2