![]() |
ShellExecute() >> WindowHandle
Moin, Moin.
Ein kleines Problem, welches sich hartnäckig allen Lösungsversuchen widersetzt. Aufrufs eines Links (im Beispiel das Programm calc.exe) mit ShellExecute. Anschließend benötige ich das Fensterhandle des gestarteten Programms. Mein Lösungsansatz ist folgender:
Delphi-Quellcode:
Laut MS gibt ShellExecute() als Funktionsergebnis das "instance handle of the application" zurück. Nach meiner Interpretation das "instance handle" des mit ShellExecute() aufgerufenen Programms. Nun muss das "window handle" gesucht werden. Dies versuche ich mit der Funktion GetWinHandle() zu erledigen. Funktioniert nur leider nicht.
function GetWinHandle(HInstance:LongInt):HWnd;
var TmpHWnd : HWnd; WinCaption : PChar; begin GetMem(WinCaption,255); // für test-anzeige Result :=0; TmpHWnd:=FindWindow(nil,nil); while TmpHWnd<>0 do begin GetWindowText(TmpHWnd,WinCaption,255); // test-anzeige if WinCaption<>'' then ShowMessage('Find=' +IntToStr(HInstance) +#13#13+ 'HWnd=' +IntToStr(TmpHWnd) +#13+ 'HInst='+IntToStr(GetWindowLong(TmpHWnd,GWL_HINSTANCE))+#13+ 'WinCaption='+WinCaption); if GetWindowLong(TmpHWnd,GWL_HINSTANCE)=HInstance then begin Result:=TmpHWnd; Break; end; TmpHWnd:=GetWindow(TmpHWnd,GW_HWNDNEXT) end; end; procedure ExecuteFile; var HInstance : LongInt; WndHnd : HWnd; begin HInstance:=ShellExecute(Self.Handle,'open',PChar('calc.exe'),nil,nil,SW_ShowNormal); WndHnd :=GetWinHandle(HInstance); // ... end; begin ExecuteFile end; GetWinHandle() findet zwar das gesuchte Fenster und ermittelt auch das korrekte Fensterhandle - aber der Vergleich mit dem Übergabe-Parameter "HINstance" wird nie wahr!? Warum? Das Funktionsergebnis von ShellExecute() ist im meinem Beispiel der Wert 42 - der korrekte Wert von HInstance ist aber laut GetWinHandle() = 16 777 216. Hmm, wo liegt mein Denkfehler? |
Re: ShellExecute() >> WindowHandle
Ich glaube CreateProcess liefert das Handle zurück, also brauchst du da nicht zu triksen.
Ich würde dir gerne mit etwas Code aushelfen, aber selbst nutze ich die Funktion selten. Aber hier im Forum wirst du paar Beispiele finden. |
Re: ShellExecute() >> WindowHandle
Bei der Gelegenheit ein kleiner Trick. Wenn du ein Zeilenumbruch haben willst, dann ist ^j einfacher als #13#10.
Also nicht
Delphi-Quellcode:
sondern einfacher
ShowMessage('Hallo' + #13#10 + 'Welt!');
Delphi-Quellcode:
Für zwei Zeilen: ^j^j
ShowMessage('Hallo' + ^j + 'Welt!');
Delphi-Quellcode:
ShowMessage('Hallo' + ^j^j + 'Welt!');
|
Re: ShellExecute() >> WindowHandle
Moin Ralph,
und wenn Du ShellExecuteEx verwendest und anschließend mit EnumThreadWindows die Fenster durchgehst? Nur so eine Idee. [edit] Achnee, dazu brauchst Du das Threadhandle, das bekommst Du mit ShellExecuteInfo nicht (oder ich hab es übersehen). Also doch CreateProcess oder andersrum machen: mit EnumWindows und GetWindowThreadProcessId durchiterieren und vergleichen. Da dürfte die erste Möglichkeit aber vermutlich performanter sein. [/edit] |
Re: ShellExecute() >> WindowHandle
Zitat:
Zitat:
Grundsätzlich gibt es drei Ansätze, für die du alle ShellExecuteEx oder CreateProcess verwenden musst: EnumWindows verwenden und jedes Mal die Prozess-ID überprüfen (hat DeddyH schon vorgeschlagen), mit der Toolhelp-API alle Threads des Prozesses ermitteln und alle Fenster der Threads ermitteln oder annehmen, dass der erste Thread das Fenster erzeugt und direkt EnumThreadWindows verwenden, wobei CreateProcess dir die ID dieses Threads gibt. |
Re: ShellExecute() >> WindowHandle
Bei der Aufgabenstellung gibt es ein grundsätzliches Problem. Es gibt nicht DAS Fenster bei einem Prozess. Ein Prozess kann auch gar keine Fenster haben. Oder eben 2,3,4....
Entsprechend gibt es auch keine Funktion um das Fenster zu bekommen. Man kann sich also nur alle Fenster eines Prozesses besorgen und dann das heraus suchen was man wirklich will. @Popov: ich verwende auch lieber #13#10. Der grund ist recht einfach. Ein ^j sagt nicht darüber aus was wirklich dahinter steckt. Man weiß nicht ob es einem #13#10 entspricht, einem #13 oder nur einem #10. Wenn man wirklich explizit ein #13#10 haben will ist man auf der sicheren Seite wenn man genau das hinschreibt. Andernfalls kann es einem passieren das bei der nächstne Delphiversion das ^j plötzlich für etwas anderes steht. |
Re: ShellExecute() >> WindowHandle
Vielen Dank für die Antworten!
Damit ist ShellExecute() also endgültig abserviert. Zur Aussage mit dem Instanz-Handle: Da hat mich die Delphi7-Hilfe ganz schön in die Irre geführt. Tja besser also bei MSDN nachschauen! Kernproblem ist natürlich mein "solides Halbwissen" :stupid: Ok - also CreateProcess(), dann schauen wir mal weiter :hi: |
Re: ShellExecute() >> WindowHandle
So, ich denke, ich habe es jetzt hinbekommen - die ersten Tests funktionieren zufriedenstellend. Zum besseren Verständnis zunächst die Aufgabenstellung etwas konkreter als bisher beschrieben:
In einem RichEdit sind Links auf beliebige Dateien placiert (Bilder, Texte o.ä.). Ein Klick auf die Links soll die Dateien mit dem verknüpften Programm anzeigen lassen. Damit kann man sich natürlich, ausreichend oft geklickt, auch große Bildschirme schnell "zumüllen" - aus diesem Grund soll optional der mehrfache Aufruf verhindert werden und stattdessen das bereits gestartete Programmfenster wieder in den Vordergrund geholt werden. Beispielhaft habe ich das jetzt so gelöst:
Delphi-Quellcode:
uses ShellAPI;
type PEnumInfo = ^TEnumInfo; TEnumInfo = record ProcessID : DWORD; HWND : THandle; end; tCalledLinks = record LinkText : String; WinHandle : DWord; end; var CalledLinks : tCalledLinks; // use array later on procedure AddProcess(LnkText:String;WinHandle:HWnd); begin CalledLinks.LinkText :=LnkText; CalledLinks.WinHandle:=WinHandle; (* add record to array *) end; function CanSetToForeGround(LnkText:String):Boolean; begin Result:=SetForeGroundWindow(CalledLinks.WinHandle); (* if not(Result) then begin // remove record from array end *) end; function GetAssociatedApp(FName:String):String; begin SetLength(Result,MAX_PATH); if FindExecutable(PChar(FName), nil, PChar(Result))>32 then SetLength(Result,StrLen(PChar(Result))) else Result:='' end; function RunProcess(FName:String;ShowCmd:DWORD;Wait:Boolean;var ProcID:DWord):LongWord; var StartupInfo : TStartupInfo; ProcessInfo : TProcessInformation; begin FillChar(StartupInfo,SizeOf(StartupInfo),#0); StartupInfo.cb :=SizeOf(StartupInfo); StartupInfo.dwFlags :=STARTF_USESHOWWINDOW or STARTF_FORCEONFEEDBACK; StartupInfo.wShowWindow:=ShowCmd; if not(CreateProcess(nil, @FName[1], nil, nil, False, CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInfo)) then Result:=WAIT_FAILED else begin if not(Wait) then begin WaitForInputIdle(ProcessInfo.hProcess,INFINITE); ProcID:=ProcessInfo.dwProcessId; exit end; WaitForSingleObject(ProcessInfo.hProcess,INFINITE); GetExitCodeProcess (ProcessInfo.hProcess,Result) end; if ProcessInfo.hProcess<>0 then CloseHandle(ProcessInfo.hProcess); if ProcessInfo.hThread <>0 then CloseHandle(ProcessInfo.hThread) end; function EnumWindowsProc(Wnd:DWORD;var EI:TEnumInfo):Boolean; stdcall; var PID : DWORD; begin GetWindowThreadProcessID(Wnd,@PID); Result:=(PID<>EI.ProcessID) or (not IsWindowVisible(WND)) or (not IsWindowEnabled(WND)); if not(Result) then EI.HWND:=WND end; function FindAppWindow(PID:DWORD):DWORD; var EI : TEnumInfo; begin EI.ProcessID:=PID; EI.HWND :=0; EnumWindows(@EnumWindowsProc,Integer(@EI)); Result:=EI.HWND end; procedure CallLink(DataFile:String;AdmitMultipleCalls:Boolean); var AppName, ProcessPara : String; ProcID : Cardinal; begin AppName :=GetAssociatedApp(DataFile); ProcessPara:=AppName+' '+DataFile; if AdmitMultipleCalls or not(CanSetToForeGround(DataFile)) then if RunProcess(ProcessPara, SW_SHOWNORMAL, false, ProcID)=WAIT_FAILED then ShowMessage('Create process error') else if not(AdmitMultipleCalls) then AddProcess(DataFile,FindAppWindow(ProcID)) end; procedure TfoTest15.buShowLinkClick(Sender:TObject); begin CallLink('d:\data_p\delphi\delphiguide\bitmaps\Edit_0000_Save.bmp', cbAdmitMultipleCalls.Checked) end; |
Re: ShellExecute() >> WindowHandle
Zitat:
|
Re: ShellExecute() >> WindowHandle
Moin Luckie :-D
Na, ich wollte nicht zu viel schreiben. Es ist ja keine Umorientierung, sondern eher eine Konkretisierung der Ausgangsanforderung. Auch mit den eher allgemein formulierten Angaben im ersten Post bin ich ja praktisch direkt zur Verwendung von CreateProcess() "geführt" worden. Aber du hast natürlich Recht - ich gelobe Besserung :thumb: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:32 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz