- ] Thanks to VeNoM00 [ -

     

.

01011010001010101101111001011010101011011110110101011011001001
01011010001010101101111001011010101011011110110101011011001001
01011010001010101101111001011010101011011110110101011011001001

.


-- INDICE:-- ----Inviaci una E-Mail



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