Run an application and get the handle to its window

Sometimes, when we start an application programmatically, it is useful to have a handle to it's window. The process of how to do this is described below.

1. create a new unit utils.pas:

unit utils; interface uses Controls, Graphics, StdCtrls, ExtCtrls, ComCtrls, Buttons, Dialogs, Classes, SysUtils, Windows, ShellApi, Forms; procedure CloseMessage (process_id : Cardinal); // get proc id function RunCommandEx (const Cmd, Params: String) : Cardinal; // get handle function ExecApplication(APPName, CmdLine: String; out proc_id : Cardinal): Cardinal; // for use with ExecApplication function GetHandles(ThreadID: Cardinal): Cardinal; var WindowList: TList; function GetWindow (Handle: Cardinal; LParam: longint): bool; stdcall; implementation function GetWindow (Handle: Cardinal; LParam: longint): bool; stdcall; begin Result := true; WindowList.Add (Pointer (Handle)); end; function ExecApplication(APPName, CmdLine: String; out proc_id : Cardinal): Cardinal; var StartInfo: TStartupInfo; ProcInfo: TProcessInformation; process_id : Cardinal; 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(500); process_id := ProcInfo.dwProcessId; proc_id := ProcInfo.hProcess; Result := GetHandles(process_id); // CloseHandle (ProcInfo.hProcess); CloseHandle (ProcInfo.hThread ); end; function GetHandles(ThreadID: Cardinal): Cardinal; var i: integer; hnd : Cardinal; cpid : DWord; begin Result:=0; WindowList := TList.Create; EnumWindows (@GetWindow, 0); for i := 0 to WindowList.Count - 1 do begin hnd := HWND (WindowList [i]); GetWindowThreadProcessID (hnd, @cpid); if ThreadID = CPID then begin Result := hnd; Exit; end; end; WindowList.Free; end; procedure CloseMessage (process_id : Cardinal); var StatusCode : Cardinal; begin if process_id > 0 then begin if GetExitCodeProcess (process_id, StatusCode) then begin if StatusCode = STILL_ACTIVE then TerminateProcess (process_id, StatusCode); CloseHandle (process_id); end; end; end; function RunCommandEx (const Cmd, Params: String) : Cardinal; var SEI: TShellExecuteInfo; begin result := 0; //Fill record with zero byte values FillChar(SEI, SizeOf(SEI), 0); // Set mandatory record field SEI.cbSize := SizeOf(SEI); // Ask for an open process handle SEI.fMask := see_Mask_NoCloseProcess; // Tell API which window any error dialogs should be modal to SEI.Wnd := Application.Handle; //Set up command line SEI.lpFile := PChar(Cmd); if Length (Params) > 0 then SEI.lpParameters := PChar(Params); SEI.nShow := sw_ShowNormal; // Try and launch child process. Raise exception on failure if not ShellExecuteEx(@SEI) then Abort; // Wait until process has started its main message loop WaitForInputIdle(SEI.hProcess, Infinite); result := SEI.hProcess; end; end.
2. Now lets get back to the main form and create two global variables, windows_handle and proc_id, and reference utils.pas:
type TForm1 = class(TForm) ... private { Private declarations } public { Public declarations } end; var Form1: TForm1; window_handle : cardinal; proc_id : cardinal; implementation {$R *.dfm} uses utils;
3. The application is ran as following:
procedure TForm1.cmdRunApplicationClick(Sender: TObject); var app_name : string; command_line : string; StatusCode : Cardinal; begin if proc_id > 0 then if GetExitCodeProcess (proc_id, StatusCode) then if StatusCode = STILL_ACTIVE then begin MessageDlg ('Application is already loaded', mtWarning, [mbOk], 0); exit; end; app_name := 'C:\WINDOWS\system32\notepad.exe'; command_line := ''; window_handle := ExecApplication (app_name, command_line, proc_id); end;
In the code above, we run Notepad. Here windows_handle is the handle of Notepad window. 4. put this on FormClose event:
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin CloseMessage (proc_id); end;
5. here are examples of what we can do with the application's handle:
procedure TForm1.cmdDoSomethingClick(Sender: TObject); begin if window_handle > 0 then SetWindowText (window_handle, PChar ('Hello')); // change title ShowWindow (window_handle, SW_SHOW); // activate the window PostMessage (window_handle, WM_CLOSE, 0, 0); // close the window end;