Ricavare e
impostare le proprietà di altri programmi
COSA VI
SERVIRA': Microsoft
Visual Basic 6 (la versione Learning dovrebbe
bastare) - Possibilmente MSDN (Microsoft
Devoloper Network) - Api Text Viewer (nella
cartella degli strumenti di Visual studio, se non lo
possedete cercate i valori delle operazioni nel file
winuser.h nella cartella Include di Visual C++; se non
avete neppure questo scaricatelo) - Spy++ (nella cartella degli
strumentidi Visual studio) - Notepad (Blocco
Note) - Calc (Calcolatrice di Windows) -
Progetti originali (non strettamente necessari) - (piattaforma
Windows a 32bit)
Step
1
In
questo documento vedremo come in Visual Basic attraverso
le API (=Application Programming
Interface) di Windows, cioè l'insieme delle
costanti (dichiarazioni di valori costanti), i tipi (o
strutture nel C), le dichiarazioni (di funzioni) presenti
nei principali DLL di Windows come User32.dll, advapi32.dll,
gdi32.dll eccetera, si possa ricavare o impostare
un'informazione (un valore che descrive l'oggetto a
cui si riferisce ad esempio il testo di una casella di
testo, il suo colore e il suo carattere sono delle
proprietà) di un controllo(un pulsante, una
barra degli strumenti, una casella di testo...) o di
un form (una finestra).
Ammettiamo di avere una casella di testo della quale
vogliamo sapere il numero di righe.
Per prima cosa creiamo con VB un EXE standard
e inseriamoci una TextBox (cioè una
casella di testo) con le seguenti proprietà:
Name = Prova
Height = 1335
Width = 2055
MultiLine = True
Text = Cinque righe qualsiasi
Questa è la casella di testo della quale dobbiamo
scoprire il numero di righe cioè cinque.
Ora inseriamo nel nostro progetto un modulo e
trascrivete quanto riportato:
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Analizziamo il significato di questa riga di codice:
Public = Indica che al dichiarazione è relativa
all'intero progetto e non solo a questo modulo
Declare = Indica che quanto segue si tratta di una
dichiarazione cioè implementa nel programma ciò che si
dichiara descrivendo i suoi parametri
Function = Indica che ciò che si sta dichiarando
è una funzione e che quindi deve restituire un vaolre
SendMessage = Indica il nome attraverso il quale
la funzione potrà essere invocata nel programma e, non
in questo caso, può voler dire anche il nome nella DLL
dalla quale si sta acquisendo la funzione altrimenti
definito dopo Alias
Lib "User32" = Indica il nome della DLL
nella quale si trova la funzione dichiarata
Alias "SendMessageA" = Indica che nella
DLL la funzione ha un nome diverso da quello indicato
dopo Function e specifica quale è questo nome
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam
As Long, lParam As Any) = Indica i parametri della
funzione che si sta dichiarando, analizzeremo più avanti
il significato di ognuno di questi
As Long = Indica il tipo di valore restituito
dalla funzione, Long è un numero compreso
nell'intervallo fra -2147483648 e 2147483647
La funzione che abbiamo appena dichiarato ci servirà per
inviare a un controllo a un form al richiesta di
restituire o impostare l'informazione che ci interessa(cioè
il numero di righe)
Ora dichiariamo la costante che indica il numero che
indentifica l'operazione che dobbiamo svolgere che è EM_GETLINECOUNT
mentre il suo valore in esadecimale è BA
che corrisponde a 186.
Public Const EM_GETLINECOUNT = &HBA
Dovete sapere che le possibili operazioni sono migliaia e
ognuna a un suo valore, il nome, che potete cambiare a
vostro piacimento, e i valori corrispondenti si possono
trovare nel file winuser.h o nella guida di MSDN ma lo
strumento più semplice da usare è certamente l'API
Vierver che si trova nella cartella degli
strumenti di Visual Studio, apritelo, dal menu file
selezionate Carica file di testo e poi
win32api.txt. Per convertire un numero esadecimale a
normale usate la calcolatrice di Windows: selezionate Hex
incollate il numero da trasformare e poi tornate su Dec
Ora torniamo al form e inseriamo un altro TextBox, per
questo controllo impostate solamente la proprietà Name
su NumRighe; qui faremo visualizzare il
numero delle righe della TextBox Prova.
Infine inseriamo un CommandButton (un normalissimo
pulsante) al quale daremo il nome di Agisci
proprio perchè premendo questo bottone ricaveremo il
numero di righe.
Ora andiamo sul codice del form e creiamo la Sub Agisci_Click
(anche semplicemente facendo doppio click
sul bottone Agisci)
e inseriamo prima di End Sub la seguente riga:
NumRighe.Text = SendMessage(Prova.hwnd, EM_GETLINECOUNT, 0, 0)
Ora arriva il bello! Analizziamo:
NumRighe.Text = = Indica il testo della casella di
testo NumRighe deve diventare come il
rusltato dell'espressione che segue
SendMessage = Invoca il metodo dichiarato nel
modulo
(Prova.hWnd, = corrisponde al parametro della
dichiarazione (ByVal hwnd As Long, cioè dobbiamo
fornire a questa funzione l'handle del controllo o del
form sul quale vogliamo interagire. L'handle è un numero
Long che possiedono tutti i controlli, form e persino il
desktop; esso serve per farli identificare da altri
programmi. Esso può cambiare durante l'esecuzione di un
programma. In VB ricavare l'handle di un controllo è
necessario utilizzare la proprietà hWnd(Prova.hWnd)
EM_GETLINECOUNT, = corrisponde al parametro della
dichiarazione ByVal wMsg As Long, cioè dobbiamo
fornire alla funzione l'operazione che deve eseguire ma
attenzione EM_GETLINECOUNT è una
costante il che significa che scrivere il suo nome o il
suo valore non fa alcuna differenza ma semplifica il
codice e diminuisce il tempo di sviluppo
0, = Primo parametro dell'operazione, vedremo più
avanti il suo utilizzo per ora non serve e quindi è zero
0) = Secondo parametro dell'operazione, vedremo più
avanti il suo utilizzo per ora non serve e quindi è zero
Facendo partire il programma nella TextBox NumRighe
apparirà un numero in più rispetto al numero di righe
che avevate digitato in fase di progettazione perchè
questo controllo automaticamente aggiunge una riga vuota.
Ricapitoliamo il codice:
Nel form:
Private Sub Agisci_Click()
NumRighe.Text = SendMessage(Prova.hwnd, EM_GETLINECOUNT, 0, 0)
End Sub
Nel modulo:
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public Const EM_GETLINECOUNT = &HBA
Step
2
Brucerò
piuttosto in fretta lo step 2 visto che si tratta
semplicemente di sfruttare i parametri delle operazioni
che prima avevamo tralasciato visto che l'operzione EM_GETLINECOUNT
non ne aveva. Apportiamo qulache modifica al codice...
Per prima cosa sostituiamo l'operazione con una che abbia
dei parametri: fa al caso nostro EM_GETLINE
che restituisce il testo di una riga specificata di un
controllo TextBox la cui proprietà Multiline sia
impostata su True.
Quindi nel modulo cambiamo la riga:
Public Const EM_GETLINECOUNT = &HBA
...con:
Public Const EM_GETLINE = &HC4
E nel form:
NumRighe.Text = SendMessage(Prova.hwnd, EM_GETLINECOUNT, 0, 0)
...con:
Dim kk As Long
Dim kk2 As String
kk = 2
kk2 = String(255, Chr(1))
SendMessage Prova.hwnd, EM_GETLINE, kk, ByVal kk2
NumRighe.Text = Left(kk2, InStr(1, kk2, Chr(1)) - 1)
Analizziamo ogni nuova parte:
Dim kk As Long = Dichiara la variabile kk
come un numero di tipo Long (un
numero compreso tra -2.147.483.648 e 2.147.483.6477);
questa è la variabile che indicherà il numero della
riga della TextBox che si desidera venga resituita.
Dim kk2 As String = Dichiara la variabile kk2
come String(stringa di testo con
fino a circa 2 miliardi di caratteri); questa è la
variabile nella quale verra resituita la riga desiderata.
kk = 2 = Imposta il valore della variabile kk
su 2 per restituire la seconda riga.
kk2 = String(255, Chr(1)) = Imposta il valore di kk2
su 255 caratteri uguali; essi verranno
sostituiti dalla stringa che verrà resituita; la
lunghezza di questa stringa deve essere superiore a
quella della stringa restituita.
SendMessage Prova.hwnd, EM_GETLINE, kk, ByVal kk2
= Invia un messaggio alla TextBox Prova (identificata
attraverso il suo handle grazie alla proprietà Prova.hwnd)
che gli dice di restituire la riga kk e
di metterla nella variabile kk2(questa
è l'operazione EM_GETLINE);
avrete probabilmente notato che sono sparite le parentesi
e il NumRighe.Text = questo perch[ la linea non viene
restituita direttamente dalla funzionema immessa nella
stringa kk2, naturalmente una funzioen
in quanto tale deve restituire qualcosa, in questo caso
la lunghezza della linea desiderata che a noi non
interessa e che tralasciamo e eliminando ci; che abbiamo
eliminato.
NumRighe.Text = Left(kk2, InStr(1, kk2, Chr(1)) - 1)
= Imposta il testo della TextBox NumRighe
su kk2 dall'inizio fino al primo
carattere che si ripeteva (Chr(1)).
Ricapitoliamo il codice del secondo step:
Nel form:
Private Sub Agisci_Click()
Dim kk As Long
Dim kk2 As String
kk = 2
kk2 = String(255, Chr(1))
SendMessage Prova.hwnd, EM_GETLINE, kk, ByVal kk2
NumRighe.Text = Left(kk2, InStr(1, kk2, Chr(1)) - 1)
End Sub
Nel modulo:
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public Const EM_GETLINE = &HC4
Step
3
Il
titolo di questo documento è "Ricavare e impostare
le proprietà di altri programmi".
Bene in questa sezione spiegherò come fare ciò. Fino ad
ora abbiamo utilizzato sempre la proprietà Prova.hwnd
che ci restituiva l'handle della TextBox:
SendMessage Prova.hwnd, EM_GETLINE, kk, ByVal kk2
Ma se noi volessimo modificare la TextBox (o
qualunque altro controllo) di altri programmi?
Avremmo bisogno del loro handle da sostituire a Prova.hwnd.
Come possiamo ricavare l'handle di un programma in
esecuzione? Andiamo nella cartella degli strumenti di
Visual Studio e apriamo lo Spy++ che fa
al caso nostro: cliccate sul quinto bottone della toolbar
(Find Window) e vi apparirà una finestra;
selezionate Hide Spy++ e poi cliccate
tenedo premuto sul mirino del Finder Tool
e rilasciatelo su un controllo qualsiasi di un programma
in esecuzione (anche Windows), ecco che nella
casella di testo Handle appare l'handle
del controllo sul quale avete rilasciato il mouse! Se vi
state chidendo "Come diavolo ah fatto?" la
risposta è semplice ha utilizzato la funzione WindowFromPoint
delle Api di Windows; ecco la sua dichiarazione da
inserire nel modulo:
Public Declare Function WindowFromPoint Lib "user32" Alias "WindowFromPoint" (ByVal xPoint As Long, ByVal yPoint As Long) As Long
I parametri xPoint e yPoint
indicano chiaramente la posizione sullo schermo del
controllo del quale si desidera l'handle.
Proviamo a replicare il codice che potrebbe aver
utilizzato lo Spy++:
Per prima cosa abbiamo bisogno di un qualcosa simile al
Finder Tool anche nel nostro programma, creiamo quindi
una PictureBox che chiameremo (proprietà Name)
FinderTool, ora dobbiamo impostare che
quando si rilascia il mouse su un qualsiasi controllo di
un qualsiasi programma (dopo aver premuto senza
rilasciare sul FinderTool)
ci venga restituito l'handle del controllo sul quale il
mouse è stato rilasciato attraverso la funzione WindowFromPoint
precedentemente dichiarata; le coordinate richieste da
questa funzione ci vengono fornite dalla funzione GetCursorPos:
Public Declare Function GetCursorPos Lib "user32" Alias "GetCursorPos" (lpPoint As POINTAPI) As Long
Se avete notato nei parametri troviamo lpPoint As
POINTAPI, il tipo POINTAPI va
definito:
Public Type POINTAPI
X As Long
Y As Long
End Type
Questa definizione fa in modo che una variabile definita
come POINTAPI seguita da un punto ci dia
la possibilità di accedere ad altre due "sottovaribili"
(X e Y
che sono dei valori LONG).
Ecco un esempio:
Dim kk As POINTAPI
kk.X = 1
kk.Y = 2
(non inserite questo codice nel progetto!)
Ora, l'evento che viene generato quando si rilascia il
mouse dopo aver premuto (senza rilasciare) sul FinderTool
è FinderTool_MouseUp:
Private Sub FinderTool_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
Ora che abbiamo l'handle desiderato possiamo inviare la
richiesta al controllo che ci interessa; per semplicità
continueremo a utilizzare l'operazione EM_GETLINE
dello Step2 ma con Notepad (se vi
sembra comodo aggiungete un pulsante che una volta
premuto vi farà aprire Notepad altrimenti ricordatevi di
aprirlo ad ogni avvio del programma; per aprite Notepad: Shell "Notepad.exe", vbNormalFocus
) quindi scriveteci
almeno due, tre righe qualsiasi.
Definiamo la variabile dove metteremo la posizione
corrente del mouse (funzione GetCurrentPos):
Dim kk3 As POINTAPI
Ora prendiamo la posizione corrente del cursore:
GetCursorPos kk3
Logicamente andrà modificata anche la funzione
SendMessage:
SendMessage WindowFromPoint(kk3.X, kk3.Y), EM_GETLINE, kk, ByVal kk2
Se non avete ben capito le modifiche effettuate guardando
il codice per intero del terzo Step vi sarà tutto più
chiaro:
Form:
Private Sub FinderTool_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
Dim kk As Long
Dim kk2 As String
Dim kk3 As POINTAPI
GetCursorPos kk3
kk = 3
kk2 = String(255, Chr(1))
SendMessage WindowFromPoint(kk3.X, kk3.Y), EM_GETLINE, kk, ByVal kk2
NumRighe.Text = Left(kk2, InStr(1, kk2, Chr(1)) - 1)
End Sub
Modulo:
Type POINTAPI
X As Long
Y As Long
End Type
Public Const EM_GETLINE = &HC4
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public Declare Function WindowFromPoint Lib "user32" (ByVal xPoint As Long, ByVal yPoint As Long) As Long
Public Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Step
4
Nel
quarto step creeremo un programma vero e proprio che
cambi le impostazioni di altri programmi, per renderlo più
comodo verranno utilizzate delle nozioni su input da file
e utilizzo base di controlli standard che non spiegherò
in questo documento. In questo step spiegherò l'utilizzo
di questo programma, più qualche nuova istruzione.
Per prima cosa aprite lo step 4 nel progetto (se non
lo avete ancora scaricato fatelo, il link è all'inizio
della pagina) analizziamo la finestra partendo da in
alto a sinistra:
FinderTool
(PictureBox): funziona esattamente come
nello step 3.
Handle
(TextBox): qui dopo aver cliccato sul FinderTool
verrà visualizzato l'handle di ogni controllo su
cui si passa
ClassName
(TextBox): qui verrà visualizzato il
nome della classe del controllo su cui si trova
il puntatore del mouse (vedremo poi come)
CostantNumber
(TextBox): qui viene visualizzato il numero
corrispondente all'operzione selezionata in Operazioni
che viene preso da Costanti
OnlyClass
(CheckBox): imposta se in Operazioni
visualizzare solo quelle relative alla classe
specificata in ClassName
Operazioni
(ListBox): elenco delle operazioni
presenti nel file define.txt (compreso
nel progetto, riaddatamento del file winuser.h
solo con i nomi dellae costanti e i relativi
valori)
Costanti
(ListBox): i valori relativi a Operazioni
(esso è invisibile in esecuzione)
Par1
(TextBox): imposta il primo parametro
dell'istruzione SendMessage
LongOp1
(OptionButton): invia come primo
parametro un numero Long
specificato in Par1
AscOp1
(OptionButton): invia come primo
parametro un numero che corrisponde al codice
ASCII del carattere specificato in Par1
Par2
(TextBox): imposta il secondo parametro
dell'istruzione SendMessage
LongOp2
(OptionButton): invia come secondo
parametro un numero Long
specificato in Par2
AscOp2
(OptionButton): invia come secondo
parametro un numero che corrisponde al codice
ASCII del carattere specificato in Par2
StringOp2
(OptionButton): invia come secondo
parametro la stringa specificata in Par2
LoopChr
(CheckBox): invia come secondo parametro
il ripetersi di Chr(1) per LoopNumber
volte; utilizzato per fare un buffer nel caso
l'operazione dovesse restituire una stringa
LoopNumber
(TextBox): imposta il numero di volte
che deve essere ripetuto il Chr(1)
se LoopChr è selezionato.
Longg,
Hexx, Ascc (TextBox):
scrivendo un numero in Longg
verrà "riprodotto" in esadecimale e il
relativo carattere ASCII e viceversa.
Senda
(Button): invia il messaggio al
controllo prefissato.
Resulto
(TextBox): mostra cosa ha restituito la
funzione SendMessage dopo aver
premuto Senda
ResPar1
(TextBox): mostra la variabile definita
in Par1 dopo aver premuto Senda
ResPar2
(TextBox): mostra la variabile definita
in Par2 dopo aver premuto Senda
LongOp3,
LongOp4, LongOp5,
AscOp3, AscOp4,
AscOp5, StringOp5:
impostano il tipo di dati da visualizzare nella
sovrastante TextBox
L'ultima
cosa che aggiungo è come ricavare la classe da un
handle, la dichiarazione:
Public Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
...e l'utilizzo:
GetClassName Handle.Text, kk2, kk3
kk2 = 256 volte Chr(1) (String)
kk3 = la lunghezza di kk2 (256) (Long)
In kk2 eseguita la funzione sarà presente il nome della
classe.
_ <___> _
/|\_/WhO\_/|\
||||VeNoM00||||
|/\/ |iS?| \/\|
|/ \|
TORNA INDIETRO
|