Link to home
Start Free TrialLog in
Avatar of skynergy
skynergy

asked on

Get the handle of a windows application that was launched using ShellExecute

I want to get the handle of an windows application which was launched using ShellExecute so that later I can use the SetForegroundWindow to show it again instead of launching another instance. I dont want to use the GetWindowText api call to get the handle of that aplpication. I know there is an instance handle that gets returned but it isn't the same as the actual handle of the application. Just note I am talking about launching other windows appliactions which are not delphi apps i.e. like notepad for instance. So I want to launch notepad using ShellExecute get the handle for that instance of notepad, use SetForegroundWindow to show that instance of Notepad without searching the desktop windows for a title of 'notepad' to get the handle. How do I do this? Thanx in advance!
Avatar of ZhaawZ
ZhaawZ
Flag of Latvia image

What you need is a handle of a *window*, not an application (application may have many windows, and each window/control has its own handle). I believe it could be a bit difficult to determine which is the right handle (i.e., handle of the main window of an application).
GetWindowText() has nothing to do with getting handle ;] at least it *should not* have...
ASKER CERTIFIED SOLUTION
Avatar of pcsentinel
pcsentinel

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of CodedK
When you use ShellExecute IF you using it with (sw_show)...
You could use GetForegroundWindow which will return a handle for the focused window.

You said :
>> I know there is an instance handle that gets returned but it isn't the same as the actual handle of the application.

This could happen for many reasons.
Maybe this is an ActiveX control..or
It's not mandatory in Windows that a process has to have a window ... or
As ZhaawZ said an application may have many windows.

You could use WindowFromPoint and use the mouse to get the handle of every window in the application.
Avatar of tobjectpascal
tobjectpascal

well CreateProcess launches the program returning the PID, not the window handle, then you enum all the windows then convert the window handle to a PID and then compare it to the PID which got returned by createprocess, when you find a match the handle must be that..

Avatar of skynergy

ASKER

Thank you for all the feedback guys. Give me some time to test your suggestions.

I have tried Pcsentinel's code but it doesn't bring the opened notepad to the front. Am I missing something?
I think the problem in this question is the class.

Asker is saying that he doesnt want to use FindWindow because he doesnt get the classname right.
The problem is that you are going to get the same class name you got with FindWindow what ever you try...
Maybe this is an ActiveX menu..

Please try this application :
http://www.dennisbabkin.com/php/download.php?what=WinID
unit ThreadtoWnd;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  WindowList: TList;

implementation

{$R *.DFM}

function GetWindow (Handle: HWND; LParam: longint): bool; stdcall;
begin
  Result := true;
  WindowList.Add (Pointer(Handle));
end;

Function GetHandles(ThreadID: Cardinal): Hwnd;
var i: integer;
    wnd,Hnd: HWND;
    Buffer: array [0..255] of char;
    ProcessID,CPid: DWord;
begin
  Result:=0;
  WindowList := TList.Create;
  EnumWindows(@GetWindow,Wnd);
    for i := 0 to WindowList.Count - 1 do
     begin
      Hnd := HWND (WindowList [i]);
      GetWindowText (Hnd, Buffer, SizeOf (Buffer) - 1);
      getclassname(hnd,buffer,sizeof(buffer));
      GetWindowThreadProcessID(hnd,@cpid);
      If ThreadID=CPID Then
       Begin
        Result:=Hnd;
        Form1.Memo1.Lines.Add('Handle '+IntToStr(Hnd)+' Text: '+Buffer+' TH '+IntToStr(ProcessID));
        Exit;
      End;
     end;
End;

Function ExecApplication(APPName, CmdLine: String): HWnd;
var
  StartInfo: TStartupInfo;                    ProcInfo: TProcessInformation;
begin
  FillChar(StartInfo, SizeOf(StartInfo), 0);
  StartInfo.cb:=SizeOf(StartInfo);
  StartInfo.dwFlags:=STARTF_USESHOWWINDOW;
  StartInfo.wShowWindow:=SW_Show;
  if AppName<>'' then
     CreateProcess(PChar(APPName), PChar(CmdLine), nil, nil, False, 0, nil, nil, StartInfo, ProcInfo);
 Sleep(2000);  //give notepad time to run (2 seconds)
  Result:=GetHandles(ProcInfo.dwProcessId);
  CloseHandle(ProcInfo.hProcess);
  CloseHandle(ProcInfo.hThread );
End;

procedure TForm1.Button1Click(Sender: TObject);
Var
 Wnd: Hwnd;
 N: Integer;
begin
 Wnd:=ExecApplication('c:\winnt\notepad.exe','');
 If Wnd<>0 Then
  Begin
   Sleep(3000); //pause for 3 seconds while you move notepad to the back
   SetWindowPos(Wnd,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE Or SWP_NOSIZE);
  End;
end;

end.


The only modification made is to bring it to the front using SetWindowPos, this should do exatly as you requested now.
Sorry , the showwindow will bring the window back from a minimised state,

add BringWindowToFront as in

  ShowWindow(GetMainAppWndFromPid(fPID),SW_SHOW);
  BringWindowToTop(GetMainAppWndFromPid(fPID));


regards
Great! Both (pcsentinel & tobjectpascal) examples works very well and thank you for the help so far. To CodedK & ZhaawZ in my question I referred to the Notepad application as both a window and application, maybe not the same thing as a application can have more than one window but in the case of the notepad it is. Sorry if my windows api knowledge is a bit limited so I apologize if I dont use the correct terminology. I do know how to get the handle on a window by searching for its title text. My question was more directed to get the handle of the window when I launch it so that I dont have to go and search for the title of that window. I also dont want the user of the application to specify the title of the window, it was an option but I would prefer not to. Also I dont really want to use GetForegroundWindow to get the handle of the window when I used ShellExecute with swShow because I might be launching two or more different windows in succession which would make this difficult. I do appreciate the link for WinID thank you!

Just another question. If Notepad was launched and then minimised, how can I show it using the two examples and make the foreground window. I tried:
ShowWindow(hwnd, SW_SHOWDEFAULT);
BringWindowToTop(hwnd);
but this will show it but not select it.

Thank you!
for restoring minimised window:
if GetWindowLong(hwnd, gwl_style) and ws_iconic <> 0 then ShowWindow(hwnd, sw_restore);
Thanx this still restores the window but doesn't give it focus.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ZhaawZ, the code above combined with my previous code works fine
Thank you for all the help guys, I really appreciate it!