Newsgroup: it.comp.lang.c++
Titolo: hwnd?

Ciao... ho un problemino.. vorrei creare una interfaccia per un programma
(il Winamp)
che mi permetta di caricare un programma .
Allora, nella documentazione del winamp c'e' un file Frontend che mi dice i
messaggi
da inviare al winamp per fare Play Stop Pause.... con questa metodologia
result = SendMessage(hwnd_winamp,WM_WA_IPC,command_data,command);
oppure con la Post Message...
ora io faccio il programma e tutto il resto ma quando compilo il
compilatore 
mi dice giustamente che hwnd_winamp non esiste.. non e' una variabile...
ed ha ragione! Insomma io vorrei sapere come faccio a trovare l'hwnd di 
un programma in memoria.. io per caricare il winamp userei il comando 
System("nomefile");
ma so che ne esistono altri (exec... e roba simile)... Se c'e' qualcuno che
mi vuole
anche spiegare cosa sia l'hwnd sarà per sempre ricordato (almeno da me!:-)!
Grazie ciao!!
-- 
ain...@hotmail.com

Che cos'è un hWnd:
Un hWnd (handle di finestra) è un identificatore che il sistema operativo
assegna ad ogni finestra che crea. La relazione tra un hWnd e una finestra
è biunivoca; ciò significa che un hWnd identifica una ed una sola finestra,
e che ogni finestra è identificata da un solo hWnd (in pratica significa
che gli hWnd sono tutti diversi l'uno dall'altro). Questo ti permette ad
esempio di inviare un messaggio ad una finestra identificandola solo con un
hWnd.
Come ottenere un hWnd:
Se vuoi continuare ad usare System (che non ho mai sentito nominare: usi
forse Borland?) puoi usare la funzione EnumWindows (che enumera tutte le
finestre principali presenti nel sistema al momento della chiamata) finché
non trovi quella che ha il nome di classe di Winamp (spiegazioni più
avanti).
Invece di System potresti usare CreateProcess, che tra le altre cose
restituisce un identificatore di thread. Puoi passare questo identificatore
alla funzione EnumThreadWindows, che opera più o meno come EnumWindows, ma
in un campo più ristretto. Nel caso specifico di Winamp, la finestra che ti
interessa dovrebbe essere la prima ed unica ad essere enumerata.
(prima di tutto) Che cos'è la classe di una finestra:
La classe di una finestra è un insieme di informazioni riguardanti alcune
caratteristiche prestabilite della finestra stessa. Prima che sia possibile
creare una finestra di una data classe, la classe deve essere registrata
nel sistema con un nome, che viene assegnato dallo sviluppatore
dell'applicazione (non sempre; mi pare che MFC ne assegni uno
automaticamente). Questo nome, è proprio quello che devi cercare durante
l'enumerazione per poter individuare la finestra che ti interessa. Nel caso
generale, potrebbe succedere che una o più istanze di un'applicazione (ad
esempio Winamp) siano già in esecuzione prima della chiamata a System (o
altro). In questo caso ci sarebbe più di una finestra con il nome di classe
di Winamp; dovresti quindi fare due enumerazioni: una prima di System e una
dopo; nella prima dovresti salvare gli hWnd di tutte le istanze presenti ad
esempio in un array, e nella seconda dovresti confrontare ogni hWnd (di
Winamp) con ciascuno di quelli presenti nell'array; quello che non compare
nell'array, è quello della finestra creata dal processo lanciato da te
(l'uso di EnumThreadWindows risolverebbe anche questo problema, poiché
opera solo nel contesto di un singolo thread). Nel caso specifico,
comunque, Winamp non permette l'istanziazione multipla, quindi, da questo
punto di vista, nessun problema, è sufficiente una sola enumerazione.
Come si fa a sapere il nome della classe di un'applicazione:
Si può usare ad esempio un programma come Microsoft Spy++. Se usi Borland o
altro, non ti so dire; ad ogni modo il nome della classe di Winamp è
'Winamp v1.x' (vedi anche NOTA, più avanti).
Come si usa EnumWindows:
Prototipo:
BOOL EnumWindows(
     WNDENUMPROC lpEnumFunc,
     LPARAM lParam);
Il primo parametro è l'indirizzo di una funzione callback che devi
implementare tu. E' in questa funzione che scriverai il codice per
controllare il nome della classe ed eventualmente salvare l'hWnd. Il
secondo parametro è un valore che viene passato alla funzione callback; in
questo caso si potrebbe passare l'indirizzo di una variabile di tipo HWND
in cui salvare l'hWnd.
Come si implementa la funzione callback EnumWindowsProc:
Di questa funzione (come di tutte le funzioni callback) viene fornito solo
il prototipo (non è necessario chiamarla proprio EnumWindowsProc, può avere
benissimo un altro nome, l'importante è passare poi il nome giusto a
EnumWindows):
BOOL CALLBACK EnumWindowsProc(
     HWND hWnd,
     LPARAM lParam);
Questa funzione verrà chiamata una volta per ogni finestra enumerata; il
primo parametro sarà l'hWnd della finestra e il secondo sarà il valore
lParam passato alla funzione EnumWindows. Il valore di ritorno può essere
TRUE o FALSE. Se ritorni TRUE l'enumerazione continua, se ritorni FALSE
l'enumerazione termina (una volta trovato l'hWnd che ti interessa non ha
senso continuare l'enumerazione).
Un esempio di implementazione di EnumWindowsProc:
BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam)
{
 TCHAR tszNomeClasse[50];
 GetClassName(hWnd,tszNomeClasse,50);
// Confronta il nome di classe di Winamp con
// quello della finestra attualmente in esame.
 if(0 == _tcscmp(tszNomeClasse,_T("Winamp v1.x")))
 {
  // Trovato !!!
  *(HWND *)lParam = hWnd;  // salva l'hWnd
  return FALSE;                    // interrompe l'enumerazione
 }
 return TRUE;                      // continua l'enumerazione
}
La relativa chiamata a EnumWindows:
HWND hWinampWnd;
EnumWindows(EnumWindowsProc,(LPARAM)&hWinampWnd);
Ora puoi passare hWinampWnd a SendMessage.
NOTA sulla classe di Winamp:
Il fatto che il nome di classe di Winamp sia 'Winamp v1.x' mi fa pensare
che dalla versione 2.0 in poi (io ho la 1.80) cambierà; quindi uno stretto
controllo sulla stringa (nella funzione EnumWindowsProc) darebbe problemi
in futuro. Se vuoi che il tuo programma funzioni anche con future versioni
di Winamp, sarebbe meglio effettuare un controllo meno rigido; ad esempio
potresti controllare che il nome di classe cominci con la stringa 'Winamp'
o qualche cosa del genere.
Utilizzo di CreateProcess:
CreateProcess manda in esecuzione un'applicazione; ha molti parametri, ma
vanno quasi tutti posti a 0; ecco il prototipo:
BOOL CreateProcess(
     LPCTSTR lpszApplicationName,
     LPCTSTR lpszCommandLine,
     LPSECURITY_ATTRIBUTES lpsaProcess,
     LPSECURITY_ATTRIBUTES lpsaThread,
     BOOL fInheritHandles,
     DWORD fdwCreate,
     LPVOID lpEnvironment,
     LPTSTR lpszCurDir,
     LPSTARTUPINFO lpsiStartInfo,
     LPPROCESS_INFORMATION lppiProcInfo);
Tralascio le spiegazioni e faccio subito un esempio:
STARTUPINFO si;
PROCESS_INFORMATION pi;
TCHAR tszWinamp[] = _T("c:\\winamp\\winamp.exe");
ZeroMemory(&si,sizeof(STARTUPINFO));   // Importantissimo !!!
si.cb = sizeof(STARTUPINFO);
CreateProcess(NULL,tszWinamp,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);
CloseHandle(pi.hProcess);    // Abbi fede
CloseHandle(pi.hThread);
Il valore che ci interessa è pi.dwThreadId; questo valore verrà passato a
EnumThreadWindows.
Utilizzo di EnumThreadWindows:
EnumThreadWindows si usa esattamente come EnumWindows, anche la funzione
callback è la medesima. L'unica differenza è il parametro in più:
BOOL EnumThreadWindows(
   DWORD dwThreadId,
   WNDENUMPROC lpEnumFunc,
   LPARAM lParam);
Il parametro in più è il primo, ed è l'identificatore del thread. Nel tuo
caso:
EnumThreadWindows(pi.dwThreadId,EnumWindowsProc,(LPARAM)&hWinampWnd);
Questo è tutto.
Stefano.

anche spiegare cosa sia l'hwnd sarà per sempre ricordato (almeno da me!:-)!
Il tipo HWND, definito negli header file di Windows, e` la "Handle
di una Window" -- consideralo, se vuoi, come una specie di "puntatore
opaco" ad una struttura-window, che puoi ottenere dal sistema ed al
sistema devi ri-passare per chiarire a quale window stai facendo
riferimento in una certa chiamata.  Windows e` pieno di HANDLE di
vari tipi specifici -- HWND probabilmente e` la piu` usata.
Non esiste nulla che possa essere chiamato "hwnd di un programma";
una HWND non e` mai "l'HWND di un programma", ma sempre di una
window (finestra).  Un programma puo` avere una window, ma puo`
anche averne tante, oppure nessuna, e possono cambiare col tempo.
Per "trovare una window", ci sono diversi modi, a seconda di quello
che sai relativamente alla window che vuoi trovare.  Se conosci la
classe della window, e/o il suo nome (titolo), FindWindowEx e` la
piu` semplice da usare; ad esempio, per trovare una window di top
level (non figlia di un'altra) con titolo "Ciao Pippo", classe ignota:
    HWND hLawin = FindWindowEx(0,0,0,"Ciao Pippo");
hLawin varra` 0 se nessuna finestra esistente ha quel titolo, se no
sara' l'handle della finestra che ha quel titolo (se ve n'e` piu` di una,
sara` una qualsiasi di esse).
Un approccio piu` sofisticato, se vuoi trovare tutte le window
appartenenti a un dato processo e/o thread, e` di enumerare
tutte le window top-level con EnumWindows, e per ciascuna
chiamare GetWindowThreadProcessId, confrontando i dati
che essa torna con gli ID di thread, ed eventualmente di
processo, che essa torna.  Se il processo lo hai creato tu
con CreateProcess, gli ID del processo e del suo thread
principale sono nella struttura PROCESS_INFORMATION
che la CreateProcess ti ritorna; attenzione, naturalmente,
alla sincronizzazione delle operazioni: se crei un processo
e poi subito vai a cercarne le finestre, nulla di piu` facile che
il tuo processo-creatore "corra di piu`" di quello creato per
cui, quando cerca le finestre di quest'ultimo, non sono ancora
state create, o almeno non tutte.
Sono parti decisamente avanzate delle API di Windows, e
non so se sia veramente il caso di tuffarsi per queste vie se
non si ha una buona comprensione delle API di Windows in
generale (e il fatto che tu non sapessi cos'e` un'HWND mi
fa pensare che la tua cultura in materia di API Windows possa
essere un poco scarsa), comunque, auguri!
Alex