MessageBox aus anderem Prozess bestätigen
-
Danke W. Posur, das ist es auch, was ich so mit Googles Hilfe bisher gefunden habe. Daher folgte auch mein Gedanke, diese Post in WinAPI weiter zu führen.
Ich habe mir das Spy++ besorgt und angeschaut. (Pfingsten; zwar nicht der Heilige, aber so mancher andere Geist ist in mir gefahren.)
Bisher habe ich mir auch schon eine Funktion mittels FindWindow() (für das Handel per Fenster Titel) und EnumChildWindows() aufgebaut. EnumChildWindows() deswegen, weil ich mit FindWindowsEx() nicht direkt an den Button komme. Der ist in einer tieferen Ebene. (Fenster Handle->"DirectUIHWND"->"CrtlNotifySink"->"Button", alles ClassNames)
Bisher habe ich keine andere Idee, als rekursiv durch alle Klassen zu laufen, bis ich den korrekten Fester Titel (in meinem Fall das "Abbrechen") und den korrekten ClassName habe (halt das Button). Das die Texte hierbei bekannt und korrekt sein müssen, ist klar. Auch dass, wenn es zwei Fenster mit gleichem Caption gibt, ich keine genauere Analyse bekommen, welcher der korrekt ist.
Aber immerhin ein guter Fortschritt. Sollte jemand sowas schon fertig haben und hier Code posten oder mir den einen oder anderen Tipp geben, danke ich Euch schon mal im voraus.
Ach ja, wenn meine Informationen korrekt sind, kann der Tastendruck auch durch ein PostMessage(Button, BN_CLICKED,0,0); erfolgen. Dann wird der Rest (das Sleep() und des WM_LBUTTONUP) nicht mehr gebraucht.
-
Du brauchst keine Enumeration oder Callback-Funktion.
Wenn du das Handle deines Dialogfensters gefunden hast, kannst du den Button mit FindWindowEx leicht finden, der este Parameter dabei ist das Handle des Hauptfensters.
Beispiel:
ButtonHandle = FindWindowEx (FensterHandle, NULL, "Button", "&Abbrechen");
-
Ich bekomme aber als Rückgabe ein NULL und kein Handel.
Daher habe ich mir eine kleine TestApp erstellt. Dort sind ein PageControl mit zwei Tabs, auf jedem Tab 2 Buttons, und ein Panel drauf. Das PageControl und das Panel kann ich direkt ansprechen. Die Buttons (alle mit unterschiedlichen Cptns) kann ich nicht so ansprechen. Erst wenn ich mit FindWindowsEx(Fenster, NextWindow,..) mit das Handel der Unterfenster hole.
Deswegen dachte ich sofort, ich muss durch die Fenster enumerieren, mir dann mittels Caption und Class das korrekte Handel suchen.
Sollte denn das ButtonHandle = FindWindowEx (FensterHandle, NULL, "Button", "&Abbrechen"); auch durch die Untermenge hindurch suchen und mir das erste passende Handel zurück geben?
-
Wenn du kein Handle zurückbekommst, war die Fragestellung falsch.
Du solltest dir mit Spy++ mal den Button ansehen. Dort siehst du dann sein Handle und auch das Parent-Fenster. Möglich, daß du erst das Handle des Tabs bzw. Panels finden mußt und den Button erst als Unterfenster des Panels findest. Spy++ kann dir die Hierarchie des Ganzen gut anzeigen, der Button muß so zu finden sein.
Wichtig bei der Analyse des Scheiterns ist auch, daß der Name des Buttons korrekt ist &Abbrechen ist richtig beim Unterstrich unter dem A, ansonsten heißte es einfach Abbrechen, schließlich zeigt dir Spy++ auch an, ob das Control wirklich vom Typ Button ist.
-
Genau das habe ich gemacht. Die Struktur, die mir Spy++ anzeigt, sieht in so aus:
Window 000F079C "SPS Verbindungsfehler" #32770 (Dialog) Window 0007093A "" DirectUIHWND Window 000F07EA "" CtrlNotifySink Window 001802FE "" ScrollBar Window 000E080E "" CtrlNotifySink Window 0005093E "" ScrollBar Window 00060942 "" CtrlNotifySink Window 000B08EC "" ScrollBar Window 00040938 "" CtrlNotifySink Window 00050590 "" ScrollBar Window 0005058E "" CtrlNotifySink Window 0005058C "" SysLink Window 0004058A "" CtrlNotifySink Window 00040586 "" SysLink Window 00040588 "" CtrlNotifySink Window 00050584 "Reconnect" Button Window 000E0840 "" CtrlNotifySink Window 00080874 "Abbrechen" Button
Die Fragestellung ist also mit "Button" und "Abbrechen" korrekt.
Dennoch bekomme ich das Null zurück und daher wollte ich die Enumeration mit EnumChildWindows() verwenden. Unter Umständen weiß ich ja nicht, wie "tief" ich suchen muss.Mir macht es nur ein wenig Sorgen, dass diese Routine eventuell sehr lange braucht, um das gesuchte zu finden. Das können ja (z.B. bei eine Application) hunderte oder gar tausende Fenster sein. Da ich das aber eigentlich nur auf das Dialog Fenster loslassen will, sollte die Suchzeit unerheblich sein.
-
HWND Fehlerdialog = FindWindowEx (NULL, NULL, "#32770", "SPS Verbindungsfehler");
HWND AbbruchButton = FindWindowEx (Fehlerdialog, NULL, "Button", "Abbrechen");Fehlerdialog ist dann das richtige Oberfenster-Handle, AbbruchButton liefert aber NULL ?
-
Genau so ist es. Ich habe es extra nochmals mit Deinen Angaben getestet.
Das "Fehlerdialog" Handle wurde mit 0x0014078C (halt die akt. Adr.) und das "AbbruchButton" mit NULL beantwortet.Das ist aber nicht nur im Dialog so, sondern auch in meinem kleinen Test Application. Dort bekomme ich mit FindWindwEx() auch ein NULL, wenn ich nicht im passenden ChildWindow bin.
Ach ja, Windows 7, C++ Builder XE (inkl. SP und Patches) und Unicode.Ich glaube zwar nicht, dass das von Entscheidung ist, aber jetzt ist es halt mal gesagt.
Und da ich Unicode verwende, habe ich die Texte in _T() eingekleidet.Habe aber auch schon mit w_chart[] gearbeitet, nur um auf Nr. sicher zu gehen.
-
Dann probier mal, ob dies funktioniert:
HWND Fehlerdialog = FindWindowEx (NULL, NULL, "#32770", "SPS Verbindungsfehler");
HWND DirectUI = FindWindowEx (Fehlerdialog, NULL, "DirectUIHWND", NULL);
HWND Notify = FindWindowEx (DirectUI, NULL, "CtrlNotifySink", NULL);
for (int i = 0; i < 7; i ++)
Notify = GetNextWindow (Notify, GW_HWNDNEXT);
HWND AbbruchButton = FindWindowEx (Notify, NULL, "Button", "Abbrechen");
-
Ja, das funktioniert einwandfrei, hier kann ich sauber jedes einzelne Handel, mit Spy++ vergleichend, sehen bzw durchlaufen.
Das entspricht auch meinen Erfahrungen mit meinem Test Programm.Was mir halt an diesem Aufbau nicht gefällt, ist die Tatsache, das ich doch schon relativ genaue Infos über den Aufbau des (in meinem Fall vom TaskDlg) "fremden" Programms haben muss. (Die Namen, die Anzahl nötiger GetNextWindow() usw.)
Ich wollte mir eine allgemeine Lösung aufbauen um an jedes Handel in jeder Anwendung zu kommen. Ausgefiltert nach Caption und ClassName.
Jedoch habe ich so eben eine Lösung für mein spezielles Problem gefunden. Das TaskDlg scheint eine "richtig" Message Behandlung zu haben. Zumindest habe ich in der WinAPI Hilfe diesen Eintrag gefunden: http://msdn.microsoft.com/en-us/library/windows/desktop/bb787499(v=vs.85).aspx
Dem nach kann man durch:
HWND Fehlerdialog = FindWindowEx (NULL, NULL, _T("#32770"), _T("SPS Verbindungsfehler")); PostMessage(Fehlerdialog, TDM_CLICK_BUTTON, ID_CANCEL,0);
die Botschaften Behandlung direkt ansprechen, wenn die ID bekannt ist.
Ich werde mir aber dennoch die oben genannte Funktion fertigstellen. Ich denke, dafür werde ich bestimmt noch einen Anwendungsfall bekommen. Nicht immer wird ja die ID bekannt sein, oder in fremden Prg's gleich bleiben.
Ich danke Dir für Deine Hilfe. Wenn ich die Funktion fertig habe, werde ich die hier posten. Das kann aber eine Zeit lang dauern, da ich ein paar sehr dringende Aufgabe vorziehen muss.
-
Okay,
war mir nicht klar, daß dein Fenster nicht vom BCB sondern von .NET kommt. Dann solltest du tatsächlich mit der dafür vorgesehenen Funktion arbeiten.
-
Hmm, mit ist/war nicht bewusst, dass der TaskDialog von .Net stammt.
Im Builder ist der in der Tool-Palette unter Vista-Dialogfelder als TTaskDialog und den habe ich in Verwendung. Generiert zur Laufzeit mit TTaskDialog *TaskDlg = new TTaskDialog(NULL);
Doch das Verhalten, dass man im korrekten ChildWindow sein muss um das Handel zu erhalten, dass habe ich in keinem Forum, Hilfe oder Google Eintrag gesehen. Oder ich habe es übersehen.
Jedenfalls kann mit einem einfachen ButtonHandle = FindWindowEx (FensterHandle, NULL, "Button", "&Abbrechen"); hier nicht gearbeitet werden, da es eindeutig zu einem Null führt. Und das hat noch nicht einmal was mit dem taskDialog zu tun, dass Verhalten habe ich auch mit der Test Application.
Ich finde die ganze Sache schon sehr verwunderlich.
Ich danke Dir für Deine Hilfe. Vielleicht findest Du ja mal ein paar Minuten und kannst den Vorgang mal bei Dir wiederholen. Würde mich schon interessieren, ob es bei anderen auch so ist.
-
Ist wahrscheinlich gar kein .NET, dein Link hat mich nur verwirrt.
TTaskDialog kannte ich nicht, gibt es erst ab Vista, brauche hier aber noch XP Kompatibilität und arbeite mit älteren Builder-Versionen. Schein ein Dialog der WINAPI zu sein ähnlich einer Message-Box nur mit mehr Möglichkeiten.Bei deinem Code war allerdings &Abbrechen nicht richtig, es muß dort Abbrechen stehen (gemäß Spy++). Du kannst ja mal statt "Abbrechen" NULL eingeben und schauen, ob dann der erste Button gefunden wird.
-
Das mit dem "&Abbrechen" hatte ich beim einfügen schon erkannt. (hatte aus Deinem vorherigem Post und nicht aus meinem Code kopiert, sorry für die Verwirrung)
Wenn ich das Caption auf NULL stelle, bekomme ich das Handel vom gleichen Button zurück. Ist aber eigentlich auch klar, da ist ja nur ein Button im ChildWindow. Wenn es zwei wären, denke ich, würde ich das Handle vom ersten bekommen. Da weißt man dann nicht genau, welches es ist.
Daher will ich ja eine Filtermaske mit ClassName und Caption. Mehr Angaben habe ich ja nicht (aus einem Fremden Prozess) und das es in einem Windows zwei gleiche Button mit gleichem Text gibt, macht ja nicht viel Sinn.
Wenn es jedoch zwei Window Element gibt, die gleichen Text in mehreren Buttons haben, habe ich Pech. (z.B. zwei Panels mit 2 gleichen Buttons)War eigentlich der Beitrag jetzt OffTopic? Immerhin haben wir ja nur WinAPI Funktionen besprochen.
P.S. Du hast recht, TTaskDialog ist (nach alter Borland Manier) eine Kapselung von TaskDialog aus der WinAPI:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb756938.aspx
-
Okay, die Sache ist ja jetzt gelöst.
War BCB using WINAPI, also nicht off topic.
Die direkte Funktion des TaskDialogs ist sicherlich die beste Lösung.
Alternativ kann man auch mit EnumChildWindows arbeiten (benötigt eine Callback-Funktion, die z. B. die Caption prüft), bei den wenigen Child Windows kann das auch nicht lange dauern.