Ich habe vor längerem für KeePass [1] ein Plugin [2] geschrieben welches
PuTTY's "pageant" implementiert.
Konzept ist gleich wie bei pageant.exe: KeePass läuft im Hintergrund und
hat die SSH Keys gespeichert. Fordert ein Programm (z.B.) PuTTY eine
Authentifizierung mit Agent an, so sendet dieses eine WM_COPYDATA
Nachricht an ein Fenster mit dem Titel/Klasse "Pageant".
Einziger Unterschied: Der Workspace (mit den Keys) von KeePass kann aus
Sicherheitsgründen "gelockt" sein. Im Falle von "Auto-Type" fordert
KeePass nun automatisch zur Eingabe des Passwortes auf. Genau das
gleiche soll auch der Fall sein, wenn ein SSH Key benötigt wird. Also
prüft mein Plugin zuerst, ob der Workspace gelockt ist, und falls ja,
wird eine Aufforderung zum Entsperren gesendet.
Das funktioniert soweit alles - beim ersten Mal sogar perfekt: Startet
man PuTTY und der Workspace ist gelockt, fordert KeePass zur Eingabe des
Masterpasswortes auf - der Dialog ist ganz oben - "foreground" window.
Leider funktioniert das in unregelmäßigen Abständen, sehr oft beim
weiteren Lock/Unlock nicht mehr. Der Dialog erscheint zwar, aber im
Hintergrund - trotz SetForegroundWindow. Man muss ihn mühevoll mit
ALT+TAB in den Vordergrund zaubern :-(
Zur Implementierung: Mittels MFC erstelle ich ein passendes Fenster und
überschreibe die WM_COPYDATA Behandlung:
BOOL CPageantWnd::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* cds)
{
[...]
// check if workspace is locked and try to unlock
if(!UnlockKeePass())
return FALSE;
[...]
}
Die Unlock Routine sieht so aus:
BOOL CPageantWnd::UnlockKeePass()
{
// g_pAPI is the KeePass plugin API. IsFileOpen()
// tells if the workspace is locked.
if(g_pAPI->IsFileOpen())
return TRUE;
// get current forground window. Should be the window
// requesting authentication, e.g. PuTTY
CWnd *p = CWnd::GetForegroundWindow();
// Get HWND from KeePass via API
CWnd *hKeePass = CWnd::FromHandle(g_pAPI->GetMainWindowHandle());
if(hKeePass) {
// brings main window of KeePass in front whose child window
// is the following password dialog
hKeePass->SetForegroundWindow();
// sends the same message to KeePass which would have been sent
// via File -> "Unlock Workspace". The password dialog for
// unlocking the workspace appears.
// Since the plugin runs in the same thread as KeePass, we need
// to use SendMessage to wait until unlocking has completed
hKeePass->SendMessage(WM_COMMAND, ID_FILE_LOCK);
}
// now we can put previous window into foreground again (e.g. PuTTY)
if(p)
p->SetForegroundWindow();
if(g_pAPI->IsFileOpen())
return TRUE;
return FALSE;
}
Normalerweise müsste alles stimmen. SetForegroundWindow() liefert keinen
Fehler zurück und die HWNDs sind gültig. Ich habe schon alles mögliche
versucht und hab jetzt keine Idee mehr. Auch der Autor von KeePass weiss
nicht weiter.
Hat irgendjemand eine Idee wo der Fehler liegen könnte? Muss man noch
irgendwas beachten?
Zum Nachvollziehen reicht es einfach, die Programme [1,2]
herunterzuladen ;-), eine KeePass Datei anzulegen und drinnen einem
Eintrag einen SSH Key anhängen.
Danke && LG,
Niki
[1] http://www.keepass.info
[2] http://www.keepass.info/plugins.html#puttyagent
On 8/15/2011 11:02 PM, Niki Hammler wrote:
> <snip>
> Normalerweise müsste alles stimmen. SetForegroundWindow() liefert keinen
> Fehler zurück und die HWNDs sind gültig. Ich habe schon alles mögliche
> versucht und hab jetzt keine Idee mehr. Auch der Autor von KeePass weiss
> nicht weiter.
>
> Hat irgendjemand eine Idee wo der Fehler liegen könnte? Muss man noch
> irgendwas beachten?
>
Das ist eben das Verhalten von SetForegroundWindow, dass es den Fokus
nicht mehr stehlen darf. Du machst eigentlich schon alles richtig, aber
dasjenige Programm, das gerade den Keyboardfokus hat, muesste Deinem
Programm erlauben (mit AllowSetForegroundWindow), den Fokus zu bekommen,
und das kann ja schliesslich ein beliebiges Programm sein.
Es gibt mindestens zwei schmutzige Wege, Deinem Programm den Fokus zu
geben, und garantiert nennt in diesem Thread jetzt irgendeiner
mindestens einen dieser Wege (ich will mich hier eigentlich
zurueckhalten, aehem...).
Aber vielleicht kannst Du ja auch eine andere Loesung verfolgen: Mach
Deinen Dialog topmost, dann liegt er in der z-Order vor der Anwendung
mit dem aktuellen Fokus, nur hat er den Fokus nicht, das muss der
Anwender dann selbst besorgen mit draufklicken. Oder zeige ein Balloon
Tooltip in der tray area an und wenn der Benutzer auf den Balloon
Tooltip draufklickt, zeigst Du Deinen Dialog an.
--
S
SetForegroundWindow wird evtl. nicht ausgeführt, damit verhindert wird,
dass ein Benutzer in seiner Arbeit unterbrochen wird.
Wenn Du SetForegroundWindow erzwingen willst musst DU Dich mit
AttachThreadInput an das aktuelle aktive Fenster attachen.
--
Martin Richter [MVP] WWJD http://blog.m-ri.de
"A well-written program is its own heaven; a poorly written
program is its own hell!" The Tao of Programming
FAQ: http://www.mpdvc.de Samples: http://www.codeproject.com
Du benutzt Windows 7? AFAIK funktioniert das mit dem ForegroundWindow da
nicht mehr.
> Du benutzt Windows 7? AFAIK funktioniert das mit dem ForegroundWindow da
> nicht mehr.
Es gibt Wege...
Greetings
Jochen
Am 16.08.2011 10:04, schrieb Martin Richter [MVP]:
> SetForegroundWindow wird evtl. nicht ausgeführt, damit verhindert wird,
> dass ein Benutzer in seiner Arbeit unterbrochen wird.
> Wenn Du SetForegroundWindow erzwingen willst musst DU Dich mit
> AttachThreadInput an das aktuelle aktive Fenster attachen.
Danke für eure Antworten!
Die Argumente verstehe ich gut und glaube, dass es die bessere Lösung
ist, den "Pageant" Teil separat laufen zu lassen und einen Balloon-Tip
anzuzeigen. Das habe ich nun so umgesetzt: Der Agent läuft in einem
eigenen Thread und wartet auf WM_COPYDATA. In diesem Fall zeige ich
einen Balloon-Tip an und warte auf ein Event. Parallel wird das
Hauptfenster von KeePass ge-sub-classed und ich fange Nachrichten
NIN_BALLOONTIMEOUT, NIN_BALLOONHIDE und NIN_BALLOONUSERCLICK auf, die
das Event setzen. Letzterer zeigt zusätzlich den Passwortdialog an:
[...]
case NIN_BALLOONUSERCLICK:
SetForegroundWindowEx(g_hKeePass);
::SendMessage(g_hKeePass, WM_COMMAND, ID_FILE_LOCK, 0);
pEvent.SetEvent();
break;
[...]
SetForegroundWindowEx ist die Funktion von [1] und verwendet
AttachThreadInput.
Leider kann es daran nicht ganz liegen, denn es funktioniert noch immer
nicht:
1.) SetForegroundWindowEx(g_hKeePass): Hier habe ich gemerkt, dass es
genau dann nicht funktioniert, wenn ich die Option aktiviert habe, dass
sich KeePass ins Tray minimiert (statt in die Taskleiste). In diesem
Fall ist das Fenster unsichtbar und SetForegroundWindow() liefert
tatsächlich FALSE. Gibt es hier einen Workaround dass
SetForegroundWindow auch mit versteckten Fenstern funktioniert? Oder
kann das Problem wo anders liegen? In jedem Fall habe ich nicht die HWND
des Passwortdialogs ansich sondern nur die des Parents (MFC Frame Fenster).
2.) Problematischer ist eigentlich das zurückfokussieren auf das
urspüngliche Fenster; z.B. PuTTY. Die Routine in WM_COPYDATA (anderer
Thread als KeePass!) sieht nun ca. so aus:
// Get active window, e.g. PuTTY
CWnd *pCurrentWnd = CWnd::GetForegroundWindow();
SubclassKeePass();
// Show balloon and wait for user to click (or dimiss)
ShowBalloon("An application requested SSH pubkey", "Click here to unlock
workspace...");
// wait 40s for the event
::WaitForSingleObject(pEvent.m_hObject, 40000);
pEvent.ResetEvent();
UnsubclassKeePass();
// set original window (e.g. PuTTY) to foreground again
if(pCurrentWnd)
SetForegroundWindowEx(pCurrentWnd->m_hWnd);
und das funktioniert nicht: Ich klicke auf den Balloon; KeePass bzw. der
Passwortdialog kommt in den Vordergrund. Die Titelleiste von PuTTY geht
von blau auf grau; die des Passwortdialogs wird blau. Ich gebe das
Masterpasswort ein (oder breche mit ESC ab). Genau danach sollte
SetForegroundWindowEx auf das PuTTY Fenster ausgeführt werden. Die
Titelleiste von PuTTY bleibt jedoch grau ... Ist hier vielleicht doch
noch etwas zu beachten?
Danke && LG
Niki
Am 16.08.2011 07:58, schrieb Stefan Kuhr:
> [...]
> Das ist eben das Verhalten von SetForegroundWindow, dass es den Fokus
> nicht mehr stehlen darf. Du machst eigentlich schon alles richtig, aber
> dasjenige Programm, das gerade den Keyboardfokus hat, muesste Deinem
> Programm erlauben (mit AllowSetForegroundWindow), den Fokus zu bekommen,
> und das kann ja schliesslich ein beliebiges Programm sein.
Leider ist das Problem noch immer nicht gelöst (siehe Antwort an Martin) :(
> [...]
> Aber vielleicht kannst Du ja auch eine andere Loesung verfolgen: Mach
> Deinen Dialog topmost,
Geht nicht, ich schreibe nur ein Plugin für KeePass. Den Dialog macht
KeePass.
> [...] Oder zeige ein Balloon
> Tooltip in der tray area an und wenn der Benutzer auf den Balloon
> Tooltip draufklickt, zeigst Du Deinen Dialog an.
Das ist unabhängig vom Problem eine gute Idee die ich jetzt
implementiert habe. Aber wie gesagt ist das Problem leider damit noch
nicht behoben. Denn ich muss ja auch nach dem Klick auf den Balloon
dafür sorgen dass der Dialog in den Vordergrund kommt.
LG
Niki
On 8/23/2011 11:39 PM, Niki Hammler wrote:
> Hallo!
>
> <snip>
> und das funktioniert nicht: Ich klicke auf den Balloon; KeePass bzw. der
> Passwortdialog kommt in den Vordergrund. Die Titelleiste von PuTTY geht
> von blau auf grau; die des Passwortdialogs wird blau. Ich gebe das
> Masterpasswort ein (oder breche mit ESC ab). Genau danach sollte
> SetForegroundWindowEx auf das PuTTY Fenster ausgefᅵhrt werden. Die
> Titelleiste von PuTTY bleibt jedoch grau ... Ist hier vielleicht doch
> noch etwas zu beachten?
>
Wahrscheinlich habe ich Deine ganze Konstellation noch nicht komplett
verstanden. Aber wenn putty in einem anderen Prozess laeuft, aber Dein
Prozess den Fokus hat, dann musst Du aus Deinem Prozess dem anderen
Prozess, also putty, gestatten, den Fokus zu bekommen, bevor Du
SetForegroundWindow aufrufst. Das geht mit AllowSetForegroundWindow. Ob
das allerdings mit Prozessen, die in einem Konsolenfenster ablaufen, wie
etwa putty, auch funktioniert, weiss ich nicht.
PS: Brauchst Du wirklich diesen Mist mit dem AttachThreadInput und
SetForegroundWindowEx um Deinem Dialog den Fokus zu geben nach Klick auf
den Balloontooltip? Das sollte doch auch einfach so funktionieren.
--
S
> PS: Brauchst Du wirklich diesen Mist mit dem AttachThreadInput und
> SetForegroundWindowEx um Deinem Dialog den Fokus zu geben nach Klick auf
> den Balloontooltip? Das sollte doch auch einfach so funktionieren.
+1, denn wir haben ja alle Fenster hier in einem Prozess...
> 1.) SetForegroundWindowEx(g_hKeePass): Hier habe ich gemerkt, dass es
> genau dann nicht funktioniert, wenn ich die Option aktiviert habe, dass
> sich KeePass ins Tray minimiert (statt in die Taskleiste). In diesem
> Fall ist das Fenster unsichtbar und SetForegroundWindow() liefert
> tatsächlich FALSE. Gibt es hier einen Workaround dass
> SetForegroundWindow auch mit versteckten Fenstern funktioniert? Oder
> kann das Problem wo anders liegen? In jedem Fall habe ich nicht die HWND
> des Passwortdialogs ansich sondern nur die des Parents (MFC Frame Fenster).
Versteckte und minimierte Fenster können nie in den Foreground. Dann
müsstest Du das Fenster mit SW_RESTORE wieder herstellen.
On 8/24/2011 11:49 AM, Martin Richter [MVP] wrote:
> Hallo Stefan!
>
>> PS: Brauchst Du wirklich diesen Mist mit dem AttachThreadInput und
>> SetForegroundWindowEx um Deinem Dialog den Fokus zu geben nach Klick auf
>> den Balloontooltip? Das sollte doch auch einfach so funktionieren.
>
> +1, denn wir haben ja alle Fenster hier in einem Prozess...
>
>
Ich habe diesen Kommentar von Dir nicht verstanden. Was bedeutet "+1"?
--
S
Martin ist auch dieser Meinung. +1 ist sowas wie "Stimmt", "Sehe ich auch so"...
C.
Hmm, auch das geht leider nicht (muss mir allerdings genauer ansehen was
beim Tray-Minimieren genau passiert).
Auf alle Fälle habe ich ja das nicht triviale Problem, dass der
Passwortdialog eben nicht das Fenster ist, dessen HWND ich habe, sondern
der Dialog vom Prozess dieses Fensters erstellt wird.
Es folgt nun die erweiterte (und vollständige) Aufgabenstellung:
In MFC Nomenklatur gibt es in KeePass die Klasse CPwSafeDlg, abgeleitet
von CDialog. *Dieses* HWND habe ich in meinem Plugin. Der eigentliche
Passwortdialog ist CPasswordDlg, ebenfalls von CDialog abgeleitet.
In CPdSafeDlg gibt es nun:
ON_COMMAND(ID_FILE_LOCK, OnFileLock)
zum Unlocken des Workspaces. Es sieht stark vereinfach so aus:
void CPwSafeDlg::OnFileLock()
{
[...]
CPasswordDlg *pDlg2 = new CPasswordDlg();
[...]
pDlg2->DoModal();
// unlock the workspace if password was correct
}
Genau diesen Dialog möchte ich beim Klick auf den Balloon im Vordergrund
haben.
Jetzt mache ich halt einfach
SendMessage(hWnd_von_CPwSafeDlg, WM_COMMAND, ID_FILE_LOCK, 0);
Das funktioniert, nur habe ich keinen Einfluss mehr auf den Dialog. Ich
könnte auch PostMessage verwenden, versuchen per FindWindow() das HWND
des Dialogs zu finden und darauf SetForegroundWindow auszuführen. Da
aber alles im gleichen Thread abläuft ist die Synchronisation sehr
schwierig wenn nicht unmöglich (teilweise hab ich es schon erfolglos
ausprobiert).
Aber irgendwie muss das doch zuverlässig möglich sein ...
Gibt es vielleicht die Möglichkeit, das auf Prozessebene zu regeln (d.h.
das KeePass.exe kurzzeitig alle Fenster im Vordergrund öffnet)?
Notfalls auch das aktuelle Fenster zuerst minimieren?
Auf alle Fälle ist nun hoffentlich verständlich auf was ich hinaus will.
Hat jemand eine Idee?
LG
Niki
Danke für den Hinweis! Es geht aber lustigerweise noch immer nicht :-(
PuTTY ist keine Konsolenanwendung, sollte daher kein Problem sein.
Kann es eventuell daran liegen dass SetForegroundWindow nicht
funktioniert wenn das Fenster nicht reagiert? Das wäre sehr sub-optimal
... ich dachte in diesem Fall wird eine Nachricht abgelegt die danach
dann abgearbeitet wird?
Hintergrund: PuTTY macht ein SendMessage(WM_COPYDATA, ...) zu meinem
Fenster. Da es keine Möglichkeit gibt PuTTY zu sagen "Ich kann grad
nicht, versuche es in 5 sekunden wieder" muss ich in meiner WndProc die
ganze Arbeit ausführen und genau hier rufe ich auch das
SetForegroundWindow auf PuTTY aus. PuTTY "hängt" also kurzzeitig, da es
ja auf die Rückkehr von SendMessage wartet.
Kann das vielleicht irgendwie ein Problem sein? Falls ja, wie könnte man
das lösen?
> PS: Brauchst Du wirklich diesen Mist mit dem AttachThreadInput und
> SetForegroundWindowEx
Schau, ich will dass es wenigstens mal irgendwie funktioniert um den
Fehler einzugrenzen.
Wenn mir klar ist, dass ich es nicht brauche kommt es natürlich auch
wieder weg. Es geht mir hier eher darum zu zeigen, dass es auch nicht an
AttachThreadInput liegen kann.
> um Deinem Dialog den Fokus zu geben nach Klick auf
> den Balloontooltip? Das sollte doch auch einfach so funktionieren.
Nein, tut es leider nicht :(
Manchmal schon, manchmal wird der Dialog aber auch im Hintergrund geöffnet.
LG
Niki
On 8/26/2011 10:26 PM, Niki Hammler wrote:
> <snip>
> Kann es eventuell daran liegen dass SetForegroundWindow nicht
> funktioniert wenn das Fenster nicht reagiert? Das wäre sehr sub-optimal
> ... ich dachte in diesem Fall wird eine Nachricht abgelegt die danach
> dann abgearbeitet wird?
>
> Hintergrund: PuTTY macht ein SendMessage(WM_COPYDATA, ...) zu meinem
> Fenster. Da es keine Möglichkeit gibt PuTTY zu sagen "Ich kann grad
> nicht, versuche es in 5 sekunden wieder" muss ich in meiner WndProc die
> ganze Arbeit ausführen und genau hier rufe ich auch das
> SetForegroundWindow auf PuTTY aus. PuTTY "hängt" also kurzzeitig, da es
> ja auf die Rückkehr von SendMessage wartet.
>
> Kann das vielleicht irgendwie ein Problem sein? Falls ja, wie könnte man
> das lösen?
>
Das hoert sich schluessig an, was Du da schreibst. Zumindest gibt es
dann in putty einen thread, der so lange blockiert, bis das aus putty
aufgerufene SendMessage (was ja synchron ist) zurueckkehrt. Wenn dieser
thread derjenige ist, in dem auch die Message Queue fuer das Fenster
laeuft (oder mit seiner input queue daran attached ist), das Du nach
vorne bringen willst, dann kann ich mir durchaus vorstellen, dass das
das Problem ist. Das laesst sich sicher mit zwei kleinen Testanwendungen
leicht nachstellen.
Wenn das das Problem ist, dann musst Du eben diese beiden Aktionen
irgendwie zeitlich entkoppeln, keine Ahnung ob das dann noch
funktionieren kann. So dass eben der SendMessage zu putty zurueckkehren
kann und danach der SetForegroundWindow gelingen kann. Ich an Deiner
Stelle wuerde mal mit zwei kleinen Testanwendungen Deine Vermutung zu
veri- oder falsifizieren versuchen.
--
S
Also kurzer Status und Überblick. Im Endeffekt habe ich zwei Probleme
mit SetForegroundWindow
(1) Einmal möchte ich aus dem Hintergrund das Fenster des eigenen
Prozesses in den Fordergrund bringen
(2) Danach (wenn das eigene Fenster geschlossen ist) möchte ich das
Fenster eines anderen Prozesses (das zuvor das Foreground window war -
die HWND wurde zuvor mit GetForegroundWindow gesichert) wieder in den
Vordergrund bringen ("zwingen"?).
Ohne es verschreien zu wollen: (1) scheint nun endlich zu funktionieren!
:-) Hier geht es wie gesagt darum, beim Klick auf einen Balloon einen
Dialog in den Vordergrund zu bekommen. Leider brauche ich dazu
AttachThreadInput, sonst funktioniert es /nicht/. Mein Fehler war glaube
ich, dass ich "idAttach" für AttachThreadInput mittels
GetForegroundWindow geholt habe. Aber offenbar ist zu diesem Zeitpunkt
kein Foregroundwindow aktiv, zumindest nicht das eigene. Deswegen
verwende ich einfach GetCurrentThreadId() - das scheint zu tun.
<meinung>
Auch wenn diese Lösung bei euch wahrscheinlich eher auf Ablehnung stößt:
Wenn der User auf einen Balloon klickt, darf er erwarten, dass etwas
anderes in den Vordergrund rückt. Das ist finde ich
benutzerfreundlicher, als es erscheint "irgendwo" im Hintergrund ein
neues Fenster. Das wirkt dann irgendwie "kaputt".
</meinung>
Aber für (2) versuche ich es nun seit Tagen vergeblich. Alle meine
Versuchen lassen das Fenster des alten Prozesses in der Taskleiste
aufblinken.
Es funktioniert nicht mit
(a) AllowSetForegroundWindow. Das erfordert laut Remarks, dass der
eigene Prozess die Rechte hat, das Foregroundfenster zu setzen.
Womöglich habe ich diese nicht ... Wie werde ich übrigens "foreground
process"?
(b) AttachThreadInput - sowohl mit GetCurrentThreadId() als auch der
Thread ID des GetForegroundWindow. Womöglich ist eben gar kein
Foregroundwindow aktiv. Das kann leicht sein, da das eigene Fenster ja
soeben geschlossen wurde.
Gibt es die Möglichkeit, davon unabhängig ein fremdes Fenster in den
Vordergrund zu "zwingen"? Möglichst ohne dass der eigene Prozess ein
(aktives) Fenster oder sogar eine Messagequeue haben muss?
Am 29.08.2011 09:29, schrieb Stefan Kuhr:
> Hallo Niki,
>
> On 8/26/2011 10:26 PM, Niki Hammler wrote:
>> <snip>
>> Kann es eventuell daran liegen dass SetForegroundWindow nicht
>> funktioniert wenn das Fenster nicht reagiert? Das wäre sehr sub-optimal
>> ... ich dachte in diesem Fall wird eine Nachricht abgelegt die danach
>> dann abgearbeitet wird?
>>
>> Hintergrund: PuTTY macht ein SendMessage(WM_COPYDATA, ...) zu meinem
>> Fenster. Da es keine Möglichkeit gibt PuTTY zu sagen "Ich kann grad
>> nicht, versuche es in 5 sekunden wieder" muss ich in meiner WndProc die
>> ganze Arbeit ausführen und genau hier rufe ich auch das
>> SetForegroundWindow auf PuTTY aus. PuTTY "hängt" also kurzzeitig, da es
>> ja auf die Rückkehr von SendMessage wartet.
>>
>> Kann das vielleicht irgendwie ein Problem sein? Falls ja, wie könnte man
>> das lösen?
>
> Das hoert sich schluessig an, was Du da schreibst. Zumindest gibt es
> dann in putty einen thread, der so lange blockiert, bis das aus putty
> aufgerufene SendMessage (was ja synchron ist) zurueckkehrt. Wenn dieser
> thread derjenige ist, in dem auch die Message Queue fuer das Fenster
> laeuft (oder mit seiner input queue daran attached ist), das Du nach
> vorne bringen willst, dann kann ich mir durchaus vorstellen, dass das
> das Problem ist.
> [...]
Das ist es glaub ich doch nicht - aus zweierlei Gründen:
(a) Ich habe nun versucht, statt in der Benahlungsroutine für
WM_COPYDATA direkt SetForegroundWindow auszuführen, einen Thread zu
starten, der eine Dummymessage per SendMessage schickt. Dieses müsste
nun solange blockieren, bis WM_COPYDATA abgearbeitet ist. Danach warte
ich sogar noch etwas mit Sleep - das gibt aber keine Änderungen
(b) Das Fenster blinkt in der Tastleiste. Das ist Indiz, dass
SetForegroundWindow zwar angekommen ist, jedoch die Rechte fehlen es
wirklich zum Foregroundwindow zu machen ...
LG
Niki
Am 31.08.2011 20:29, schrieb Niki Hammler:
> Hallo,
> [...]
> (2) Danach (wenn das eigene Fenster geschlossen ist) möchte ich das
> Fenster eines anderen Prozesses (das zuvor das Foreground window war -
> die HWND wurde zuvor mit GetForegroundWindow gesichert) wieder in den
> Vordergrund bringen ("zwingen"?).
Kleine Anmerkung/Korrektur: In diesem Fall funktioniert das
SetForegroundWindow auch ohne AttachThreadInput oder
AllowSetForegroundWindow.
Was nicht funktioniert: Wenn ich auf das [x] im Balloon klicke. In
diesem Fall verliert das derzeit aktive Fenster den Fokus (Titelleiste
wird grau). Ein folgendes SetForegroundWindow (ob mit AttachThreadInput
oder ohne ist egal) bringt das das Fenster in der Taskleiste zu blinken.
Was passiert genau wenn man auf das Balloon-[x] klickt? Wieso verliert
das aktive Fenster den Fokus? Wie kann ich ihm diesen wieder zurückgeben?
LG
Niki
On 8/31/2011 8:29 PM, Niki Hammler wrote:
> <snip>
> Ohne es verschreien zu wollen: (1) scheint nun endlich zu funktionieren!
> :-) Hier geht es wie gesagt darum, beim Klick auf einen Balloon einen
> Dialog in den Vordergrund zu bekommen. Leider brauche ich dazu
> AttachThreadInput, sonst funktioniert es /nicht/. Mein Fehler war glaube
> ich, dass ich "idAttach" für AttachThreadInput mittels
> GetForegroundWindow geholt habe. Aber offenbar ist zu diesem Zeitpunkt
> kein Foregroundwindow aktiv, zumindest nicht das eigene. Deswegen
> verwende ich einfach GetCurrentThreadId() - das scheint zu tun.
>
Ja genau. idAttach ist Dein current thread und idAttachTo ist der Thread
in dem fremden Prozess. Den kannst Du Dir mit GetWindowThreadProcessId
holen.
>
> Es funktioniert nicht mit
>
> (a) AllowSetForegroundWindow. Das erfordert laut Remarks, dass der
> eigene Prozess die Rechte hat, das Foregroundfenster zu setzen.
> Womöglich habe ich diese nicht ... Wie werde ich übrigens "foreground
> process"?
Indem Dein Prozess den Keyboardfokus hat, mal ganz unwissenschaftlich
ausgedrueckt.
>
> Gibt es die Möglichkeit, davon unabhängig ein fremdes Fenster in den
> Vordergrund zu "zwingen"? Möglichst ohne dass der eigene Prozess ein
> (aktives) Fenster oder sogar eine Messagequeue haben muss?
Na gut. SwitchToThisWindow. Bitte genau die Doku lesen.
--
S
Ich verstehe nicht, warum Du eine Reaktion auf den Klick auf das
Balloon-[x] erwartest. Warum baust Du denn nicht Code ein der auf den
Klick *in* den Balloon reagiert? Vielleicht verstehe ich aber auch das
Szenario nicht...
--
S
BOOL CALLBACK CMainApp::ActivateChildProcess(HWND hWnd, LPARAM lParam)
{
DWORD dwID;
GetWindowThreadProcessId(hWnd, &dwID);
if (dwID == (DWORD)lParam)
{
// Note: This block is adapted from CFrameWnd::ActivateFrame
// and from CFrameWnd::BringToTop.
int nCmdShow = -1;
HWND hWndLastPop;
// translate default nCmdShow (-1)
if (nCmdShow == -1)
{
if (IsIconic(hWnd))
nCmdShow = SW_RESTORE;
}
ASSERT(nCmdShow != SW_HIDE &&
nCmdShow != SW_MINIMIZE && nCmdShow != SW_SHOWMINNOACTIVE &&
nCmdShow != SW_SHOWNA && nCmdShow != SW_SHOWNOACTIVATE);
// bring to top before showing
// if no last active popup, it will return hWnd
hWndLastPop = ::GetLastActivePopup(hWnd);
::BringWindowToTop(hWndLastPop);
if (nCmdShow != -1)
{
// show the window as specified
ShowWindow(hWnd, nCmdShow);
// and finally, bring to top after showing
// if no last active popup, it will return hWnd
hWndLastPop = ::GetLastActivePopup(hWnd);
::BringWindowToTop(hWndLastPop);
}
SetFocus(hWnd);
}
return TRUE;
}
wobei ActivateChildProcess mit einem EnumWindows, das die ProcessID
des Child-Prozesses übergeben bekommt, aufgerufen wird:
void CMainApp::OnInvokeChildProcess()
{
PROCESS_INFORMATION pi;
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
if (WaitForSingleObject(m_hChildProcess, 0) != WAIT_TIMEOUT)
{
// child process handle is not valid, i.e. child process has
// not been created yet or has been closed yet
if (CreateProcess(NULL, "ChildProcess.exe /e",
NULL, NULL,
FALSE, 0,
NULL, NULL,
&si, &pi))
{
// Close the old process handle
if (m_hChildProcess)
{
VERIFY(CloseHandle(m_hChildProcess));
}
// Close the thread handle (process handle will be closed in
ExitInstance)
VERIFY(CloseHandle(pi.hThread));
// store child process information
m_hChildProcess = pi.hProcess;
m_dwChildProcessId = pi.dwProcessId;
}
}
else
{
// child process handle is still valid, i.e. child process has
// been created and has not been closed yet
// ActivateChildProcess() activates all top-level windows whose PID
matches m_dwChildProcessId.
EnumWindows(CMainApp::ActivateChildProcess,
(LPARAM)m_dwChildProcessId);
}
WaitForInputIdle(m_hChildProcess, INFINITE);
//ShellExecute(NULL, "open", pDoc->m_strPathNameBlah, NULL, NULL, 0);
// OpenChildProcessDoc() sends WM_COPYDATA to all top-level windows
whose PID matches m_dwChildProcessId.
EnumWindows(CMainApp::OpenChildProcessDoc,
(LPARAM)m_dwChildProcessId);
}
Keine Ahnung, ob das bei neueren Windowsen auch ncoh geht.
HTH
Danke für die Antwort ...
Am 01.09.2011 09:06, schrieb Stefan Kuhr:
> [...]
> Ich verstehe nicht, warum Du eine Reaktion auf den Klick auf das
> Balloon-[x] erwartest. Warum baust Du denn nicht Code ein der auf den
> Klick *in* den Balloon reagiert? Vielleicht verstehe ich aber auch das
> Szenario nicht...
Womöglich ...
Ich reagiere auch auf den Klick *in* den Balloon. Das scheint derzeit
tatsächlich zu funktionieren (ohne es verschreien zu wollen).
Allerdings würde ich mir erwarten dass das Wegklicken des Balloons mit
dem [x] dem aktuellen Fenster nicht den Fokus stiehlt.
Folgendes Szenario: Ich verbinde mich mit PuTTY zu einem Server, dessen
private key ich nicht in KeePass gespeichert habe; der Workspace ist
gelockt.
PuTTY ist aktiv, es sendet WM_COPYDATA an KeePass was dazu führt dass
ich den Balloon sehe. Nachdem ich weiss, dass der key eh nicht drinnen
ist (und ich daher den Workspace nicht unlocken muss), klicke ich den
Balloon mit [x] weg. Natürlich erwarte ich mir, dass PuTTY danach noch
immer den Fokus hat.
Dem ist aber nicht so; ich muss mühevoll mit der Maus wieder aufs
Fenster klicken. Ich verstehe nicht, wieso mit der Balloon den Fokus
stiehlt. Wohl gemerkt: In diesem Fall führe ich nie ein
SetForegroundWindow/AllowSetForegroundWindow/AttachThreadInput aus ...
LG,
Niki
On 9/1/2011 7:21 PM, Niki Hammler wrote:
> <snip>
> Allerdings würde ich mir erwarten dass das Wegklicken des Balloons mit
> dem [x] dem aktuellen Fenster nicht den Fokus stiehlt.
>
Naja, also ich schon. Das Balloon-Tooltip ist eben auch nur ein Fenster.
Das kriegt eben kurzzeitig vermutlich kurzzeitig den Fokus, im Moment,
wo Du draufklickst. Und damit ist der Fokus anderswo weg.
> Folgendes Szenario: Ich verbinde mich mit PuTTY zu einem Server, dessen
> private key ich nicht in KeePass gespeichert habe; der Workspace ist
> gelockt.
>
> PuTTY ist aktiv, es sendet WM_COPYDATA an KeePass was dazu führt dass
> ich den Balloon sehe. Nachdem ich weiss, dass der key eh nicht drinnen
> ist (und ich daher den Workspace nicht unlocken muss), klicke ich den
> Balloon mit [x] weg. Natürlich erwarte ich mir, dass PuTTY danach noch
> immer den Fokus hat.
>
> Dem ist aber nicht so; ich muss mühevoll mit der Maus wieder aufs
> Fenster klicken. Ich verstehe nicht, wieso mit der Balloon den Fokus
> stiehlt. Wohl gemerkt: In diesem Fall führe ich nie ein
> SetForegroundWindow/AllowSetForegroundWindow/AttachThreadInput aus ...
>
Hhmmm, ist vielleicht eine komplett andere Loesung denkbar? Statt einem
Balloontooltip so ein Fenster, wie das bei Firefox, wenn der Download
endet oder sowas? Oder ist das nicht unter Deiner Kontrolle? Denkbar
waere dann, dass Du dann so ein Fenster (anders als ein Balloontooltip
aus Shell_NotifyIcon) subclassen koenntest und zumindest den Moment des
Wegklickens mitkriegen wuerdest, dann mit fiesen Tricks das putty wieder
zum Foreground-Window machen.
Also zumindest in MFC-Next gibt es dafuer ein paar huebsche Klassen, mit
denen man solche Fenster schoen erzeugen kann.
Nur so ne Idee...
--
S