Prozess im Vordergrund von eigener Anwendung erstellen?
-
Hallo!
Seit Kurzem versuche ich einen Prozess zu starten, dessen Formular im Vordergrund (vor meiner Anwendung) bleiben soll, bis dieser beendet wird.
Es geht mir nicht darum mit "SetWindowPos" oder ähnlichem das Formular einmalig in den Vordergrund zu setzen. Z. B. gibt es bei der Funktion "MessageBox" als ersten Parameter das Handle (HWND hWnd), welches sich auf das Handle einer Windows Form bezieht. Wird das Handle einer Form angegeben, erscheint die MessageBox vor dem Formular, und lässt sich ohne Schließen der MessageBox auch nicht wegklicken. Klickt man auf das Hauptformular (dessen Handle angegeben wurde) ertönt ein MessageBeep, und die MessageBox blinkt auf.Gibt es eine Möglichkeit dieses Verfahren bei einem Formular eines Prozesses anzuwenden, von dem ich die ProcessID und das Handle vom Formular habe?
Vielen Dank,
Noctua-Dev
-
WS_TOPMOST?
-
Danke für die Antwort!
ist "WS_EX_TOPMOST" nicht nur für CreateWindow?
Kann man damit auch das Formular eines "fremden" Prozesses ändern?
Den Prozess starte ich mit CreateProcess...
-
Noctua-Dev schrieb:
Danke für die Antwort!
ist "WS_EX_TOPMOST" nicht nur für CreateWindow?
Kann man damit auch das Formular eines "fremden" Prozesses ändern?
Den Prozess starte ich mit CreateProcess...Mit SetWindowPos kannst Du jedenfalls auch Topmost ändern. Ob das auch mit fremden Fenstern geht, hab ich allerdings nicht ausprobiert.
-
Habe es gerade getestet:
Mit WS_EX_TOPMOST setzt man das Fenster (was zu dem Handle gehört) in den Vordergrund. Leider verschwindet das Fenster beim Anklicken des anderen Fensters wieder im Hintergrund.
-
@Noctua-Dev
Du kannst mal versuchen ob es mitSetParent
funktioniert: https://msdn.microsoft.com/en-us/library/windows/desktop/ms633541.aspx
Ist aber auf jeden Fall ein Hack - würde ich eher nicht empfehlen.Ansonsten kannst du periodisch nachgucken ob das Fenster des anderen Programms hinter deines verschoben wurde, und bei Bedarf erneut
SetWindowPos
aufrufen.@mgaeckler
Soweit ich weiss gehtSetWindowPos
auch mit fremden Fenstern. Kann höchstens sein dass da Permissions geprüft werden. Wenn der fremde Prozess aber vom eigenen gestartet wurde, dann läuft er auch üblicherweise mit dem selben Account, und dann sollte es diesbezüglich keine Probleme geben.
-
Ich arbeite gerade mit dem Borland c++ Builder 6.0.
Wenn ich die Funktion SetParent aufrufe kann ich als Parameter nur "TWinControl *" angeben. Bei einem Handle erhalte ich diesen Fehler:[C++ Fehler] Unit1.cpp(24): E2034 Konvertierung von 'void * *' nach 'TWinControl *' nicht möglich
Obwohl unter https://msdn.microsoft.com/en-us/library/windows/desktop/ms633541(v=vs.85).aspx die Angabe zweier Handles möglich sein sollte??
Selbst wenn ich z.B. ein weiteres Formular (TWinControl angebe, befindet sich dies "in" meinem Hauptformular.
-
Noctua-Dev schrieb:
Ich arbeite gerade mit dem Borland c++ Builder 6.0.
Wenn ich die Funktion SetParent aufrufe kann ich als Parameter nur "TWinControl *" angeben. Bei einem Handle erhalte ich diesen Fehler:[C++ Fehler] Unit1.cpp(24): E2034 Konvertierung von 'void * *' nach 'TWinControl *' nicht möglich
Obwohl unter https://msdn.microsoft.com/en-us/library/windows/desktop/ms633541(v=vs.85).aspx die Angabe zweier Handles möglich sein sollte??
Selbst wenn ich z.B. ein weiteres Formular (TWinControl angebe, befindet sich dies "in" meinem Hauptformular.
Nimm SetParent nicht von der vcl. ::SetParent (...);
-
@Noctua-Dev
Wasmgaeckler
dir damit sagen möchte:
Die VCL-Fensterklasse definiert eine eigene Funktion dieSetParent
heisst. Weil das für reine VCL-Anwendungen bequemer ist wenn man einfach denTWinControl
-Zeiger übergeben kann statt sich das Fensterhandle raussuchen zu müssen.Und wenn du statt dessen die
SetParent
Funktion aus dem globalen Namespace aufrufen möchtest, dann musst du zwei Doppelpunkte vorn dranschreiben, also::SetParent
.ps:
Ich hab' mir angewöhnt überall wo ich WinAPI Funktionen aufrufe immer::Funktionsname
zu schreiben. Dadurch sieht man u.A. auch gleich wo direkt auf die WinAPI zugegriffen wird. Muss man aber nicht machen, ich erwähnte das nur weil's thematisch gerade dazupasst.
-
Danke!
Rufe jetzt richtige Funktion auf. Trotzdem befindet sich jetzt das Fenster in dem Formular... Gibt es auch eine Möglichkeit das Fenster vor das Formular zu setzen, oder wird bei einer MessageBox kontrolliert ob die Meldung noch im Vordergrund ist?
-
Naja, kann sein dass es mit
SetParent
doch nicht geht.Was du machen willst wäre das Owner-Window zu ändern, nicht das Parent-Window.
An einigen Stellen in der WinAPI werden für Parent und Owner die gleichen Funktionen/Member/Parameter verwendet, daher dachte ich es sollte mitSetParent
funktionieren. Muss aber nicht sein dass das so ist, ich hab's nicht ausprobiert.Bzw. bist du sicher dass das Fenster der anderen Anwendung ein Top-Level Fenster ist? Wenn du
SetParent
auf ein Child-Window aufrufst, dann ist klar dass es danach IN deiner Form drinnen ist, und nicht davor -- weil halt Child Window.
-
Aaaaaah,
hier
http://stackoverflow.com/questions/133122/how-to-change-a-window-owner-using-its-handle
steht dass manSetWindowLongPtr(..., GWLP_HWNDPARENT, ...)
verwenden kann.Das könntest du mal probieren.
-
Das sieht vielversprechend aus!
http://stackoverflow.com/questions/133122/how-to-change-a-window-owner-using-its-handleSetWindowLongPtr( win32window, GWL_HWNDPARENT, formhandle );
Problem ist nur: laut MSDN ist die Funktion wie folgt deklariert:
LONG_PTR WINAPI SetWindowLongPtr( _In_ HWND hWnd, _In_ int nIndex, _In_ LONG_PTR dwNewLong );
Der dritte Parameter (dwNewLong) sollte laut Beispiel das Handle des Formulars (formhandle) sein. Natürlich kann ich als long kein HWND übergeben.
[C++ Fehler]: E2034 Konvertierung von 'void *' nach 'long' nicht möglich
Wie kann ich trotzdem das Handle des Formulars übergeben?
-
Einfach casten.
reinterpret_cast<LONG_PTR>(deinHwndWert)
Der
reinterpret_cast
ist in dem Fall auch kein Hack, sondern vom MS ganz klar so vorgesehen.Wichtig ist nur dass du die "
...Ptr
" Funktion verwendest und auchLONG_PTR
beim Casten verwendest, sonst bekommst du ein Problem wenn du mal für 64 Bit kompilierst.
-
ps:
Wichtig:
GWL
**P
**_HWNDPARENT
stattGWL_HWNDPARENT
verwenden wenn duSetWindowLong
**Ptr
**verwendest.Sonst wird das mit 64 Bit vermutlich wieder net richtig funktionieren
-
Vielen Dank!! Funktioniert herrlich!!!
Nur noch eine Kleinigkeit:
Das andere Fenster sitzt im Vordergrund und minimiert sich auch mit. Man kann aber trotzdem noch das Parent-Formular bedienen. Muss man wirklich einen Timer oder Thread erstellen, der ein MessageBeep ausführt und den Fokus auf das Formular setzt, sobald man auf das Hauptformular klickt, oder kann man da auch mit der WinAPI arbeiten?Trotzdem: Schön dass es funktioniert!!
-
Welches Parent-Formular?
Verstehe grad nicht was du meinst.
Das Parent-Formular in deiner Applikation? Oder in der Fremden?Mach vielleicht mal Screenshots die das ganze verdeutlichen.
Bzw. falls es um deine Applikation geht, kannst du natürlich auch hier Window-Ownership verwenden. Und/oder modale Dialoge.
ps: Das ist immer noch ein Hack, und ich würde es nur begrenzt empfehlen. Es kann durchaus Applikationen geben die ... anfangen komische Dinge zu tun wenn man an ihren Fenstern in dieser Art rumpfuscht - also Owner-Window ändert oder dergleichen.
pps: Das "ist in dem Fall auch kein Hack" oben bezieht sich auf den
reinterpret_cast
, nicht auf das Ändern des Owner-Windows.
-
Hier sind die Links zu den Filmen, die hoffentlich das Problem näher führen.
Film1 (meine Anwendung):
http://www.noctua-development.de-info.de/downloads/Meine_Anwendung.wmvFilm2 (MessageBox Anwendung -> wie es sein sollte)
http://www.noctua-development.de-info.de/downloads/MessageBox_Beispiel.wmvNatürlich könnte man diesen Effekt selber erzielen, aber ich habe die Hoffnung noch nicht aufgegeben, dass es etwas Hübsches aus der WinAPI gibt.
-
OK, cool, ich denke jetzt hab' ich verstanden was du willst
Du willst bloss dass dein Dialog "gesperrt" ist, so lange das andere Programm läuft, korrekt?Das sollte doch einfach sein:
Mach eine kleine Dialog-Klasse, die nen Mini-Dialog ala "[...warte auf externe Anwendung...]" oder so anzeigt (theoretisch könner man den Dialog, falls gewünscht, auch komplett unsichtbar bekommen).Dieser Dialog sollte vom Benutzer nicht schliessbar sein, also die entsprechenden Handler-Funktionen überschreiben (bei MFC wären das z.B. OnCancel und OnClose).
Einfacherweise würde ich diesem Dialog dann auch die Aufgabe übertragen das externe Programm zu starten, und zu überwachen wann das externe Programm fertig wird (z.B. einfach nen Timer und dann mit
WaitForSingleObject(hProcess, 0)
periodisch checken ob der Prozess noch läuft). Und wenn der externe Prozess fertig ist, schliesst sich der Dialog von selbst.Und diesen Dialog rufst du dann einfach modal auf. Wodurch genau das selbe passiert wie bei einer MessageBox, nämlich dass das Fenster darunter blockiert wird.
Wobei du dir überlegen solltest ob es nicht besser wäre einfach nur alle Controls (inklusive dem Fenster-schliess-"X") zu disablen während das externe Programm läuft. Ich persönlich finde das nämlich immer nervig dass ich, wenn so ein modales Gedöns offen ist, das darunterliegende Fenster nichtmal verschieben kann.
-
ps: Du kannst natürlich auch mal probieren was passiert wenn du
EnableWindow(myDialog, false)
aufrufst.
Oder sonst ein wenig googeln ob es nen einfachen Mechanismus gibt den Effekt eines modalen Dialogs zu erreichen ohne eben einen modalen Dialog anzuzeigen.
-
Habe mich für SetParent entscheiden.
Meiner Meinung nach sauberer als modaler Dialog vor Anwendung (der nicht sichtbar wäre). Danach mit SetWindowLong und SetWindowPos in meinem Fenster positioniert.
Vielen Dank für eure HilfeVideo zur fertigen Lösung:
http://www.noctua-development.de-info.de/downloads/L%C3%B6sung.wmv