Newsgroup: it.comp.aiuto
Titolo: VB:titolo applicazioni (only for *real* programmers :)

ciao, 
nella realizzazione di un progetto col VB, mi sono imbattuto in una
incongruenza che non so bene come risolvere.
In pratica voglio controllare alcuni programmi windows tramite una
applicazione vb. Questo avviene semplicemente in viando alle varie
applicazioni e tasti che devono essere premuti (tramite la funzione
SendKey).
Prima di poter inviare i caratteri ad una applicazione, occorre
"portarla in primo piano". Per fare questo, si usa il comando
AppActivate, cui si fa seguire il titolo del programma da attivare.
Ora, il problema sta proprio qui. Uno dei programmi che voglio
controllare e' FreeAgent. In esso, il titolo e' costituito dal nome
(freeagent) affiancato dal nome del gruppo su cui si e' focalizzati
(
p.es
. it.comp.aiuto).
Insomma, in pratica il titolo, invece di essere "Free Agent" e' "Free
Agent - it.comp.aiuto". Naturlamente, AppActivate "Free Agent" *non*
funziona. Infatti l'help del comando cita:
---cut cut cut---
AppActivate titletext
The argument titletext is a string expression that is the name that
appears in the title bar of the application 
window to activate.  If there is more than one instance of the
application, the operating environment 
arbitrarily selects the one to activate.  The name that appears in the
title bar and titletext must match 
character-for-character, but the comparison is not case sensitive
---cut cut cut---
	Possibile Soluzione
Dovrebbe essere possibile riuscire a leggere il titolo di una finestra
usando una chiamata alle API. 
Ora, per lanciare l'applicazione (che verra' poi portata in primo
piano da AppActivate) uso il comando Shell (nomefile).
Questo restituisce un parametro che caratterizza l'applicazione appena
avviata. Si legge infatti sull'help:
Shell Function
If the Shell function successfully executes the named program, it
returns the instance handle of the 
started program.  The instance handle is a token identifying the
running program.  
Quindi, tramite questo "istance handle" dovrei riuscire a richiamare
l'Api adatta. 
Il problema e' che l'Api GetWindowText, vuole come parametro un certo
hwnd, definito come "handle of window". Sara' la stessa cosa dell'
istance handler precedente?
Altrimenti, posso usare
HWND GetActiveWindow(void)
The GetActiveWindow function retrieves the window handle of the active
window. 
E poi da qui risalire al titolo della finestra tramite la gia' citata
int GetWindowText(hwnd, lpsz, cbMax)
The GetWindowText function copies text of the given window's title bar
(if it has one) into a buffer. 
pero' mi sembra un modo cosi' tortuoso di procedere.
Avete (quei due o tre che sono arrivati a leggere sino a qui) qualche
suggerimento? 
 
e-mail 
pod...@iperbole.bologna.it
 (Simone & Annarita)

PO>Insomma, in pratica il titolo, invece di essere "Free Agent" e' "Free
PO>Agent - it.comp.aiuto". Naturlamente, AppActivate "Free Agent" *non*
PO>funziona. Infatti l'help del comando cita:
PO>Dovrebbe essere possibile riuscire a leggere il titolo di una finestra
PO>usando una chiamata alle API. 
..
PO>Quindi, tramite questo "istance handle" dovrei riuscire a richiamare
PO>l'Api adatta. 
??? Non capisco bene che cosa intendi. Le funzioni API si chiamano
direttamente dal programma VB, dichiarandole opportunamente nella
sezione "general - declarations" di un modulo o di una Form.
Molte funzioni richiedono come _parametro_ un Handle ad una finestra
e alcune (in genere abbastanza oscure) un Handle ad un'istanza.
PO>Il problema e' che l'Api GetWindowText, vuole come parametro un certo
PO>hwnd, definito come "handle of window". Sara' la stessa cosa dell'
PO>istance handler precedente?
No, sono due cose diverse. Un'istanza e' un'istanza e una finestra
e' una finestra. Ad un'istanza di un'applicazione possono
corrispondere piu' finestre.
PO>Altrimenti, posso usare
PO>HWND GetActiveWindow(void)
PO>The GetActiveWindow function retrieves the window handle of the
PO> active window.
Questo puo' essere certamente un inizio, se non sai a priori il nome
della finestra.
PO>E poi da qui risalire al titolo della finestra tramite la gia' citata
PO>int GetWindowText(hwnd, lpsz, cbMax)
PO>The GetWindowText function copies text of the given window's
PO> title bar (if it has one) into a buffer. 
PO>pero' mi sembra un modo cosi' tortuoso di procedere.
No, perche'? Non credo che ci siano scorciatoie, a meno che tu non
conosca a priori il nome completo della finestra (o almeno, ma e'
ancora piu' difficile, il nome della classe). In questo caso, se ti
serve l'Handle della finestra, potresti usare FindWindow.
Altrimenti non ti resta che scorrere tutte le finestre, leggendo i
titoli di oguna, in cerca di quella buona. Non dovrebbe essere
comunque un gran lavoro (e del resto non e' quello che fai anche a
mano?).
La funzione che ti serve (unitamente alla GetWindowText che
ricordavi) e' GetWindow, ovvero le piu' specifiche GetNextWindow,
GetParent, GetTopWindow:
' GetWindow() Constants
Global Const GW_HWNDFIRST = 0
Global Const GW_HWNDLAST = 1
Global Const GW_HWNDNEXT = 2
Global Const GW_HWNDPREV = 3
Global Const GW_OWNER = 4
Global Const GW_CHILD = 5
Declare Function GetWindow Lib "User" (ByVal hWnd As Integer,
     ByVal wCmd As Integer) As Integer
In sostanza, partendo da una data finestra (ottenuta, per esempio,
con GetActiveWindow) ne leggi il titolo con GetWindowText,
confrontandolo con quello da cercare. Se non corrisponde, con
GetWindow(hWnd, GW_HWNDNEXT) ottieni la finestra successiva, e cosi'
via.
Potrebbe anche essere opportuno assicurarsi (con chiamate successive
a GetParent) che la finestra da cui parti sia una finestra di "primo
livello" e non una "child".
Questa e' una cosa che ho scritto per un problema analogo al tuo.
E' in WordBasic, che usa convenzioni leggermente diverse da VB per
le dichiarazioni e il passaggio dei parametri, ma non dovrebbe
esserti difficile capire il senso della cosa e convertirlo per VB.
Declare Function GetWindow Lib "user.exe"(hFinestra As Integer,\
Comando As Integer) As Integer
Declare Function GetActiveWindow Lib "user.exe"() As Integer
Declare Function GetWindowText Lib "user.exe"(hFinestra As Integer,\
sTesto As String, LenTesto As Integer) As Integer
Function TrovaMiniDoc
Dim NomeApp$, hWnd, rc
hWnd = GetActiveWindow
hWnd = GetWindow(hWnd, 0)
While hWnd
     NomeApp$ = String$(15, " ")
     rc = GetWindowText(hWnd, NomeApp$, 12)
     If NomeApp$ = "Documentini" Then Goto Trovato
     hWnd = GetWindow(hWnd, 2)
Wend
TrovaMiniDoc = 0
Goto fine
Trovato:
TrovaMiniDoc = hWnd
fine:
End Function
Esiste anche una EnumWindows, che sembrerebbe la
funzione ideale, ma purtroppo non e' richiamabile da
VB, richiedendo come parametro il puntatore ad una
funzione callback.
Ciao.
Giovanni Zezza
zezz...@galactica.it

int GetWindowText(hwnd, lpsz, cbMax)
Per usare questa funzione occorre l'hwnd, che puoi ottenere con
EnumWindow, la quale però richiede un callback function non
realizzabile in VB.
Unica soluzione: scriverti una dll con una versione decente della
AppActivate, che però occorre realizzare con un vero linguaggio, come
C/C++ o Delphi.
Roberto.
################################################
# Roberto Paterlini                            #
# Reggio Emilia - Italy                        #
# email: 
paterlin...@iol.it
              #
#==============================================#
#  @@@   @@  @@@  @@@  @@  @@@@ @@@            #
#  @  @ @  @  @  @    @  @ @     @  Shareware  #
#  @@@  @@@@  @   @@  @  @ @@@   @     per     #
#  @    @  @  @     @ @  @ @     @   Windows   #
#  @    @  @  @  @@@   @@  @     @             #
#    Archivio Softeca - TTBase Font Manager    #
#          
http://www.bns.it/patsoft/
          #
################################################

mano?).
Alla fine, ho usato proprio questo modo.
Inizialmente, avevo usato questo codice
x=shell(prg)  'lancia il programma
x2 = GetActiveWindow()
st = Space$(100)
letti = GetWindowText(x2, st, 100)
ma, poiche' la shell *non* e' bloccante, funziona solo se i programmi
*non* hanno una finestra di avvio (come hanno invece 
p.es
. agent e
word)
Avevo provato allora a richiamare la finestra Owner di quella di
avvio:
 x3 = GetWindow(x2, GW_OWNER)
 letti = GetWindowText(x3, st, 100)
La cosa sembra funzionare se viene lanciato un solo programma, ma con
piu' comandi shell in sequenza, i risultati riportati sono errati (non
chiedermi perche', ma se lancio agent ed eudora, mi scambia i titoli
delle finestre, mentre se li lancio singolarmente tutto e' ok).
Quindi, alla fine, ho deciso di usare il metodo 'chi va piano...',
quindi scorro tutte le finestre. Non ho imparato purtroppo a creare
una lista come quella del task manager, in cui vengono riportate solo
i titoli delle finestre riguardanti i programmi aperti (e non tutte le
finestre, anche child). Cmnq, tante' ecco il codice:
 Dim st As String
 'parte dalla prima finestra
 xIniz = GetWindow(GetActiveWindow(), GW_HWNDFIRST)
' If GetParent(xIniz) <> 0 Then   |   questo servirebbe per evitare
'  xIniz = GetParent(xIniz)	   |  di scorrere le finestrre child
' End If				   |  ma si puo' evitare.
 xFine = GetWindow(xIniz, GW_HWNDLAST)
 x = xIniz
 
  Do
 'scorre  tutte le finestre
   st = Space$(GetWindowTextLength(x))
   letti = GetWindowText(x, st, GetWindowTextLength(x) + 1)
   x = GetWindow(x, GW_HWNDNEXT)
 Loop Until (x = xFine)
Le dichiarazioni delle api le potete ricavare dal file di help.
nb. e' fondamentale inizializzare la stringa (st nell'esempio) che si
passa alla funzione GetWindowText nella quale viene ritornato il
titolo della finestra. Pena misteriosi errori di violazione del
sistema (c'ho perso sopra mezz'ora, prima di notare la cosa nel tuo
codice :-((
cmnq, grazie mille per le dritte.
In effetti, usare le api e' stato meno difficile di quanto pensassi.
ps. sai dove posso trovare un po' di doc. su come utilizzare il
trumpet?
ciaociao, simone


 codice :-((
Posso togliere un po' di mistero , o almeno provarci ?
Se non ricordo  male come funziona il Basic  secondo Microsoft (ne  ho
usato solo un port  per 6502  sul VIC20  e C=64), le  stringhe vengono
gestite con un   puntatore allo heap ed   un contatore. Il primo  dato
punta allo  spazio in memoria in cui  sono memorizzati i caratteri, il
secondo contiene il numero di caratteri che compongono la stringa.
Quando si dichiara implicitamente una stringa in Basic usandola per la
prima volta  la stringa  viene inizializzata  come vuota,   ponendo il
contatore  a zero ed il  puntatore a zero o   a un valore non definito
(non  so  con  precisione,   penso  che  la   prima  ipotesi  sia piu'
probabile).
Cosa succede a Simone & Annarita se non inseriscono lo statement
st=Space$(GetWindowTextLength(x))
prima di ricuperare il nome della finestra ?
Semplicemente  questo.   GetWindowText() e`  una funzione che richiede
come secondo parametro l'indirizzo di una zona di memoria e come terzo
parametro la  dimensione in byte della zona  di memoria stessa. Se non
c'e' lo statement contenente  Space$(), il basic per  st crea solo  il
puntatore (non  si sa come  inizializzato, molto probabilmente a 0) ed
il contatore a 0.   Contatore  che pero' viene ignorato    brutalmente
passando  i   parametri  a  GetWindowText()   -viene considerato  solo
l'indirizzo-. Qundi la API si trova ad operare con un indirizzo che la
porta chissa' dove, con probabilita' nella locazione 0. E il tentativo
di accedere a questa locazione casuale genera il GPF.
o   oooooo	Gian Uberto Lauri
      o    o	
sa...@maya.dei.unipd.it
     o o