AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

ShellExecute() >> WindowHandle

Ein Thema von taaktaak · begonnen am 29. Jun 2009 · letzter Beitrag vom 30. Jun 2009
Antwort Antwort
taaktaak

Registriert seit: 25. Okt 2007
Ort: Radbruch
1.993 Beiträge
 
Delphi 7 Professional
 
#1

ShellExecute() >> WindowHandle

  Alt 29. Jun 2009, 15:36
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:
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;
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.

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?
Ralph
  Mit Zitat antworten Zitat
Popov
(Gast)

n/a Beiträge
 
#2

Re: ShellExecute() >> WindowHandle

  Alt 29. Jun 2009, 15:58
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.
  Mit Zitat antworten Zitat
Popov
(Gast)

n/a Beiträge
 
#3

Re: ShellExecute() >> WindowHandle

  Alt 29. Jun 2009, 16:07
Bei der Gelegenheit ein kleiner Trick. Wenn du ein Zeilenumbruch haben willst, dann ist ^j einfacher als #13#10.

Also nicht

ShowMessage('Hallo' + #13#10 + 'Welt!'); sondern einfacher

ShowMessage('Hallo' + ^j + 'Welt!'); Für zwei Zeilen: ^j^j

ShowMessage('Hallo' + ^j^j + 'Welt!');
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.625 Beiträge
 
Delphi 12 Athens
 
#4

Re: ShellExecute() >> WindowHandle

  Alt 29. Jun 2009, 16:12
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]
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Apollonius

Registriert seit: 16. Apr 2007
2.325 Beiträge
 
Turbo Delphi für Win32
 
#5

Re: ShellExecute() >> WindowHandle

  Alt 29. Jun 2009, 17:15
Zitat von taaktaak:
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.
Arbeitest du noch mit vor-NT-Windows? Andernfalls sagt die Dokumentation dies:
Zitat:
The return value is cast as an HINSTANCE for backward compatibility with 16-bit Windows applications. It is not a true HINSTANCE, however. It can be cast only to an int and compared to either 32 or the following error codes below.
Daneben sind HInstances Zeiger und somit prozessspezifisch. Deinen Ansatz kannst du damit auf NT-Systemen vergessen.

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.
Wer erweist der Welt einen Dienst und findet ein gutes Synonym für "Pointer"?
"An interface pointer is a pointer to a pointer. This pointer points to an array of pointers, each of which points to an interface function."
  Mit Zitat antworten Zitat
Benutzerbild von SirThornberry
SirThornberry
(Moderator)

Registriert seit: 23. Sep 2003
Ort: Bockwen
12.235 Beiträge
 
Delphi 2006 Professional
 
#6

Re: ShellExecute() >> WindowHandle

  Alt 29. Jun 2009, 17:22
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.
Jens
Mit Source ist es wie mit Kunst - Hauptsache der Künstler versteht's
  Mit Zitat antworten Zitat
taaktaak

Registriert seit: 25. Okt 2007
Ort: Radbruch
1.993 Beiträge
 
Delphi 7 Professional
 
#7

Re: ShellExecute() >> WindowHandle

  Alt 29. Jun 2009, 18:07
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"
Ok - also CreateProcess(), dann schauen wir mal weiter
Ralph
  Mit Zitat antworten Zitat
taaktaak

Registriert seit: 25. Okt 2007
Ort: Radbruch
1.993 Beiträge
 
Delphi 7 Professional
 
#8

Re: ShellExecute() >> WindowHandle

  Alt 29. Jun 2009, 22:35
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;
Ralph
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#9

Re: ShellExecute() >> WindowHandle

  Alt 29. Jun 2009, 23:52
Zitat von taaktaak:
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.
Argh, und warum kommt das erst jetzt zum Schluss? Meist du nicht auch, dass es besser ist, sowas gleich im ersten Posting mitzuteilen?
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
taaktaak

Registriert seit: 25. Okt 2007
Ort: Radbruch
1.993 Beiträge
 
Delphi 7 Professional
 
#10

Re: ShellExecute() >> WindowHandle

  Alt 30. Jun 2009, 07:31
Moin Luckie

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
Ralph
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:11 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz