SetForegroundWindow() funktioniert nicht so richtig ...

15/08/2011 - 23:02 von Niki Hammler | Report spam
Hallo,

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
 

Lesen sie die antworten

#1 Stefan Kuhr
16/08/2011 - 07:58 | Warnen spam
Hallo Niki,

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

Ähnliche fragen