mcaisco
15-12-2008, 10:57
Salve,
una premessa: non sto chiedendo come si esegue un programma esterno. Questo lo so già fare. Ho già guardato un sacco di post qui sul forum e tutti putroppo si riferiscono alla semplice esecuzione di programmi esterni.
Dunque io ho la necessità di eseguire un'applicazione esterna all'interno di una mia form, per la precisione dentro un suo pannello. In altre parole vorrei emulare una sorta di applicazione MDI in cui però le finestre child della principale (la mia applicazione) non sono form da me create, ma applicazioni esterne eterogenee, sul cui codice non ho alcun controllo quindi! Ho guardato un sacco di forum sul web e la cosa è fattibile, ma putroppo tutti gli esempi funzionanti eseguono applicazioni di windows come notepad o la calcolatrice. Queste applicazioni funzionano anche a me nella maniera voluta e questo fa ben sperare. Putroppo però non sempre le cose funzionano. Ad esempio con eseguibili come iexplore.exe (Internet Explorer) o write.exe (Wordpad) succede che l'applicazione esterna viene lanciata ma non viene "inglobata" nella mia form.
Tecnicamente quello che sul web indicano di fare è di usare alcune funzioni win-api dichiarandole esterne dentro il proprio codice "managed" in C# o C++ (o VB.NET).
Ecco un esempio del mio codice FUNZIONANTE (notate che eseguo appunto notepad.exe)
// dichiarazione delle funzioni esterne win-api
public:
[DllImport("USER32.DLL")]
static int SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
static bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
// setting del processo da lanciare
System::Diagnostics::ProcessStartInfo^ psi = gcnew System::Diagnostics::ProcessStartInfo();
psi->WindowStyle = System::Diagnostics::ProcessWindowStyle::Normal;
psi->FileName = "notepad.exe";
psi->Arguments = "";
psi->UseShellExecute = false;
System::Diagnostics::Process^ proc = System::Diagnostics::Process::Start(psi);
// il thread della mia applicazione attende INDEFINITAMENTE fino a quando l'applicazione
// esterna è stata inizializzata correttamente
bool wait = proc->WaitForInputIdle();
// imposto l'handle della finestra della nuova applicazione lanciata come child di
// un pannello della mia form
IntPtr gpsHandle = proc->MainWindowHandle;
SetParent(gpsHandle, panel3->Handle);
MoveWindow(gpsHandle, 0, 0, panel3->Width, panel3->Height, true);
this->panel3->ResumeLayout();
this->panel3->PerformLayout();
this->ResumeLayout();
this->PerformLayout();
Il codice sopra funziona correttamente. Viene lanciato il notepad e la finestra viene inglobata nel pannello della mia form. Ma se si prova a cambiare eseguibile le cose non sempre funzionano. E' come se la funzione WaitForInputIdle() non riesca sempre ad attendere il tempo necessario affinchè la nuova finestra venga inglobata. Con notepad.exe funziona. Con iexplore.exe ad esempio no. Con le applicazioni che devo utilizzare non funziona e queste semplicemente vengono eseguite ma non inglobate. Nei casi in cui le cose funzionano il valore assegnato a gpsHandle è un valore numerico positivo (suppongo un puntatore interno). Nei casi in cui non funziona, gpsHandle è 0 (ma pensa un po'...). Ho indagato anche sul ritorno di WaitForInputIdle e questa procedura ritorna true se la nuova finestra è pronta, false altrimenti. Ma nel mio caso l'attesa dovrebbe essere indefinita e quindi dle tutto bloccante. Invece la procedura ritorna sempre true, anche se poi la finestra non viene inglobata nella mi form.
Ma ecco un trucco strano. Basta sostituire la chiamata alla WaitForInputIdle() con una stupida sleep e tutto funziona. Il problema però è che io non posso sapere a priori quanto sia necessario attendere per una generica applicazione esterna da eseguire. Ho fatto delle prove e con una sleep di 300ms per ora funzionano tutte le applicazioni che ho provato. Con 100ms solo alcune... insomma è un casino!
In definitiva sembra quasi che il processo con la nuova applicazione esterna venga lanciato. Poi però il chiamante (la mia form) non rimane bloccato sulla WaitForInputIdle per un lasso di tempo sufficiente affinché l'handle della finestra associata all'applicazione esterna sia stato correttamente inizializzato. Questo sembra essere erroneo però, perchè la WaitForInputIdle, in mancanza di parametri espliciti, attende il processo chiamato per un tempo indefinito. Invece nel mio caso ritorna sempre immediatamente con valore true, come se avesse rilevato che la finestra dell'applicazione esterna sia stata inizializzata correttamente... anche se poi l'handle rimane a 0! Ovviamente ho anche provato a chiamare WaitForInputIdle passando come parametro il tempo di attesa, ma anche con valori molto alti la funzione ritorna immediatamente, fregandosene altamente insomma!
Potete aiutarmi? Qualcuno sa perchè questa procedura non fa il suo lavoro in alcune circostanze? Perchè con la sleep() le cose funzionano?
Grazie
una premessa: non sto chiedendo come si esegue un programma esterno. Questo lo so già fare. Ho già guardato un sacco di post qui sul forum e tutti putroppo si riferiscono alla semplice esecuzione di programmi esterni.
Dunque io ho la necessità di eseguire un'applicazione esterna all'interno di una mia form, per la precisione dentro un suo pannello. In altre parole vorrei emulare una sorta di applicazione MDI in cui però le finestre child della principale (la mia applicazione) non sono form da me create, ma applicazioni esterne eterogenee, sul cui codice non ho alcun controllo quindi! Ho guardato un sacco di forum sul web e la cosa è fattibile, ma putroppo tutti gli esempi funzionanti eseguono applicazioni di windows come notepad o la calcolatrice. Queste applicazioni funzionano anche a me nella maniera voluta e questo fa ben sperare. Putroppo però non sempre le cose funzionano. Ad esempio con eseguibili come iexplore.exe (Internet Explorer) o write.exe (Wordpad) succede che l'applicazione esterna viene lanciata ma non viene "inglobata" nella mia form.
Tecnicamente quello che sul web indicano di fare è di usare alcune funzioni win-api dichiarandole esterne dentro il proprio codice "managed" in C# o C++ (o VB.NET).
Ecco un esempio del mio codice FUNZIONANTE (notate che eseguo appunto notepad.exe)
// dichiarazione delle funzioni esterne win-api
public:
[DllImport("USER32.DLL")]
static int SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
static bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
// setting del processo da lanciare
System::Diagnostics::ProcessStartInfo^ psi = gcnew System::Diagnostics::ProcessStartInfo();
psi->WindowStyle = System::Diagnostics::ProcessWindowStyle::Normal;
psi->FileName = "notepad.exe";
psi->Arguments = "";
psi->UseShellExecute = false;
System::Diagnostics::Process^ proc = System::Diagnostics::Process::Start(psi);
// il thread della mia applicazione attende INDEFINITAMENTE fino a quando l'applicazione
// esterna è stata inizializzata correttamente
bool wait = proc->WaitForInputIdle();
// imposto l'handle della finestra della nuova applicazione lanciata come child di
// un pannello della mia form
IntPtr gpsHandle = proc->MainWindowHandle;
SetParent(gpsHandle, panel3->Handle);
MoveWindow(gpsHandle, 0, 0, panel3->Width, panel3->Height, true);
this->panel3->ResumeLayout();
this->panel3->PerformLayout();
this->ResumeLayout();
this->PerformLayout();
Il codice sopra funziona correttamente. Viene lanciato il notepad e la finestra viene inglobata nel pannello della mia form. Ma se si prova a cambiare eseguibile le cose non sempre funzionano. E' come se la funzione WaitForInputIdle() non riesca sempre ad attendere il tempo necessario affinchè la nuova finestra venga inglobata. Con notepad.exe funziona. Con iexplore.exe ad esempio no. Con le applicazioni che devo utilizzare non funziona e queste semplicemente vengono eseguite ma non inglobate. Nei casi in cui le cose funzionano il valore assegnato a gpsHandle è un valore numerico positivo (suppongo un puntatore interno). Nei casi in cui non funziona, gpsHandle è 0 (ma pensa un po'...). Ho indagato anche sul ritorno di WaitForInputIdle e questa procedura ritorna true se la nuova finestra è pronta, false altrimenti. Ma nel mio caso l'attesa dovrebbe essere indefinita e quindi dle tutto bloccante. Invece la procedura ritorna sempre true, anche se poi la finestra non viene inglobata nella mi form.
Ma ecco un trucco strano. Basta sostituire la chiamata alla WaitForInputIdle() con una stupida sleep e tutto funziona. Il problema però è che io non posso sapere a priori quanto sia necessario attendere per una generica applicazione esterna da eseguire. Ho fatto delle prove e con una sleep di 300ms per ora funzionano tutte le applicazioni che ho provato. Con 100ms solo alcune... insomma è un casino!
In definitiva sembra quasi che il processo con la nuova applicazione esterna venga lanciato. Poi però il chiamante (la mia form) non rimane bloccato sulla WaitForInputIdle per un lasso di tempo sufficiente affinché l'handle della finestra associata all'applicazione esterna sia stato correttamente inizializzato. Questo sembra essere erroneo però, perchè la WaitForInputIdle, in mancanza di parametri espliciti, attende il processo chiamato per un tempo indefinito. Invece nel mio caso ritorna sempre immediatamente con valore true, come se avesse rilevato che la finestra dell'applicazione esterna sia stata inizializzata correttamente... anche se poi l'handle rimane a 0! Ovviamente ho anche provato a chiamare WaitForInputIdle passando come parametro il tempo di attesa, ma anche con valori molto alti la funzione ritorna immediatamente, fregandosene altamente insomma!
Potete aiutarmi? Qualcuno sa perchè questa procedura non fa il suo lavoro in alcune circostanze? Perchè con la sleep() le cose funzionano?
Grazie