Class to Execute and Manage External EXE in your App (Views: 27)
Problem/Question/Abstract: This class allows you to Execute and Manage External Applications from within your Application. Features Execute with or without 'Wait for Completion' Get the Windows Handle of the running App. Determine if the Executed App is still running. Close the running App (Like a user would) Terminate the running App (Like 'End Task' in Task Manager) Autoclose the App if running when class Freed or your App terminates. SetFocus and Restore a running App. Usage Examples : {...} var WApp: TWinApp; {...} WApp := TWinApp.Create; WApp.ApplcationName := 'c:\winnt\notepad.exe'; WApp.parameters := 'c:\mytest.txt'; {or} WApp := TWinApp.Create('c:\winnt\notepad.exe', 'c:\mytest.txt'); {...} WApp.Execute; {...} PostMessage(WApp.Handle, WM_XXXX, ..... {...} WApp.Close {...} if WApp.IsRunning then WApp.SetFocus..... {...} if {....} then WApp.Terminate {...} WApp.Free; {WApp will close if CloseOnExit = true} This is a brand new beta class from my side, so any bugs, fixes or enhancements will be appreciated. Answer: unit MahWinExec; interface uses Windows, Messages, SysUtils, Forms; // ========================================================================== // Class to Manage External Windows Application in your Application // Mike Heydon April 2004 // // PROPERTIES // ---------- // WaitForHandle - Denotes whether to wait for windows to create and // allocate a Handle to the Main Window of the Executed // Application. Default is false. (Only neede to be true // if you really required the window handle IMMEDIATELY // after calling execute). If false the Handle will // eventually become available once the app has loaded // and created the main window. // // Handle - Returns the Handle of the Executed App's main window. // See property WaitForHandle. This can be used for API // calls such as SendMessage() and ShowWindow() or any // call that requires a valid Windows Handle (HWND). // // CloseOnExit - Denotes whether to close the Executed App (if running) // when YOUR applications end. Default is true. // // IsRunning - Read Only. Denotes whether the Executed App is still // running or not. It may have been closed by the User, // Close Method, Terminate Method etc. // // ApplicationName - Name of the App to Execute. Can also be specified at // Create time using an overloaded Create() method. // eg. 'c:\winnt\notepad.exe' // // StartDirectory - Name of the Directory to start the Application in. // Default is '' (Current Directory). // eg. 'c:\mydir' // // Parameters - Any parameters (Command Line Argument) to the App. // eg. 'c:\mydata\problems.txt' // // METHODS // ------- // Execute - Execute the Application specified by properties // ApplicationName,Parameters and StartDirectory. // If optional parameter AWaitForTerminate is set to // true then Execute will NOT return until the Executed // Application has been shut down or terminated. Default // for this parameter is false (Executes returns // immediately and execution of main thread continues) // You can also direct Execute to wait for the main // window handle to be created, see property // WaitForHandle for details. // // Close - If the App is Running then a windows message is sent // to it instruction it to close and exit. This is the // same as if the User had selected EXIT in the App. // // Terminate - If the App is Running then TerminateProcess() is // executed on the App's process ID. This is the same as // selected "End Task" in the task manager. Use it only in // extreme circumstances. The state of global data // maintained by dynamic-link libraries (DLLs) may be // compromised if Terminate is used rather than Close // // SetFocus - If Running then the Executed App is given focus. // // ========================================================================== type // TWinApp - Windows Executable App manager TWinApp = class(TObject) private FApplicationName, FStartDirectory, FParameters: string; FCloseOnExit, FWaitForHandle: boolean; FProcHandle: THandle; FStartupInfo: TStartupInfo; FProcessInfo: TProcessInformation; function GetWindowHandle: THandle; function GetAppRunning: boolean; public // Methods constructor Create; overload; constructor Create(const AApplicationName: string; AParameters: string = ''; AStartDirectory: string = ''); overload; destructor Destroy; override; function Execute(AWaitForTerminate: boolean = false): boolean; procedure Terminate; procedure Close; procedure SetFocus; // Properties property Handle: THandle read GetWindowHandle; property WaitForHandle: boolean read FWaitForHandle write FWaitForHandle; property IsRunning: boolean read GetAppRunning; property CloseOnExit: boolean read FCloseOnExit write FCloseOnExit; property ApplicationName: string read FApplicationName write FApplicationName; property Parameters: string read FParameters write FParameters; property StartDirectory: string read FStartDirectory write FStartDirectory; end; // -------------------------------------------------------------------------- implementation // ==================================== // Constructor Methods - Overloaded // ==================================== constructor TWinApp.Create; begin FProcHandle := 0; FCloseOnExit := true; FApplicationName := ''; FStartDirectory := ''; FParameters := ''; FWaitForHandle := false; end; constructor TWinApp.Create(const AApplicationName: string; AParameters: string = ''; AStartDirectory: string = ''); begin Create; // Call Standard Constructor FApplicationName := AApplicationName; FParameters := AParameters; FStartDirectory := AStartDirectory; end; // ===================================================================== // Get Handle of Main Window of Executed Application // Returns 0 if App not running or No main Window Handle. // // NOTE : If WaitForHanlde is false this function may return 0 if called // to soon after Execute(), as the Main Window may not yet have // been created and a Windows Handle allocated. If you require // the handle immediately after calling Execute then set // property WaitForHandle to true. Execute will then not return // until the Windows Handle is present. // Default for WaitForHandle is false. // ===================================================================== function TWinApp.GetWindowHandle: THandle; type PTEnumCodeData = ^TEnumCodeData; // Data struture used TEnumCodeData = record // for API CallBack Proc WindowsHandle, // EnumWindowsCode() ProcessHandle: THandle; end; var rEnumCodeData: TEnumCodeData; Retvar: THandle; // Win API Callback Function function EnumWindowsCode(Wnd: hWnd; PInfo: PTEnumCodeData): boolean; export; stdcall; var hProcess: THandle; begin GetWindowThreadProcessId(Wnd, hProcess); if PInfo^.ProcessHandle = hProcess then begin PInfo^.WindowsHandle := Wnd; Result := false; end else Result := true; end; // Start GetWindowHandle() begin if FProcHandle <> 0 then begin rEnumCodeData.ProcessHandle := FProcessInfo.dwProcessId; rEnumCodeData.WindowsHandle := 0; EnumWindows(@EnumWindowsCode, integer(@rEnumCodeData)); Retvar := rEnumCodeData.WindowsHandle; end else Retvar := 0; Result := Retvar; end; // =============================================== // Destructor Method // If property CloseOnExit is true then the // Executed Application is closed if running // =============================================== destructor TWinApp.Destroy; begin if FCloseOnExit and GetAppRunning then Close; if FProcHandle <> 0 then CloseHandle(FProcHandle); inherited Destroy; end; // ===================================== // Check if the app is running or not // ===================================== function TWinApp.GetAppRunning: boolean; var Retvar: boolean; iExitCode: DWORD; begin if FProcHandle <> 0 then begin if GetExitCodeProcess(FProcHandle, iExitCode) then Retvar := iExitCode = STILL_ACTIVE else Retvar := false; end else Retvar := false; Result := Retvar; end; // ============================== // Execute the application // ============================== function TWinApp.Execute(AWaitForTerminate: boolean = false): boolean; var Retvar: boolean; sCurrDir, sCommand: string; Wnd: THandle; begin Retvar := false; // Assume we can't execute if not GetAppRunning then begin if FProcHandle <> 0 then CloseHandle(FProcHandle); sCurrDir := GetCurrentDir; if trim(FStartDirectory) <> '' then SetCurrentDir(FStartDirectory); FParameters := trim(FParameters); FProcHandle := 0; FillChar(FStartupInfo, SizeOf(FStartupInfo), 0); FStartupInfo.wShowWindow := SW_SHOWNORMAL; FStartupInfo.cb := SizeOf(FStartupInfo); if FParameters <> '' then sCommand := trim(FApplicationName) + ' "' + FParameters + '"' else sCommand := trim(FApplicationName); if CreateProcess(nil, PChar(sCommand), nil, nil, false, 0, nil, nil, FStartupInfo, FProcessInfo) then begin // Must we wait for App to finish ? // If so then all handles are n/a if AWaitForTerminate then begin WaitForSingleObject(FProcessInfo.hProcess, INFINITE); FProcHandle := 0; end else begin // Get Main Window Process Handle // FProcessInfo.dwProcessID is NOT the handle we are // looking for. (think it is an Explorer Process FProcHandle := OpenProcess(PROCESS_ALL_ACCESS, false, FProcessInfo.dwProcessId); // Must we wait until App is loaded and has a windows handle ? // If so then stay in loop until Main Windows of App is created // and a Windows Handle has been allocated. // Default for this action is false if FWaitForHandle then while GetWindowHandle = 0 do Application.ProcessMessages; end; // Close unused handles CloseHandle(FProcessInfo.hProcess); CloseHandle(FProcessInfo.hThread); Retvar := true; end else FProcHandle := 0; SetCurrentDir(sCurrDir); end else begin if FProcHandle <> 0 then begin Wnd := GetWindowHandle; if Wnd <> 0 then begin // Focus and DeMinize App SetForegroundWindow(Wnd); ShowWindow(Wnd, SW_RESTORE); Retvar := true; end; end; end; Result := RetVar; end; // ============================================= // Ask the Application to close down normally // ============================================= procedure TWinApp.Close; var Wnd: THandle; begin if FProcHandle <> 0 then begin Wnd := GetWindowHandle; if Wnd <> 0 then PostMessage(Wnd, WM_QUIT, 0, 0); end; end; // =================================================================== // The Terminate method is used to unconditionally cause a // TWinApp to exit. Use it only in extreme circumstances. The state of // global data maintained by dynamic-link libraries (DLLs) may be // compromised if Terminate is used rather than Close // =================================================================== procedure TWinApp.Terminate; begin if FProcHandle <> 0 then TerminateProcess(FProcHandle, 0); end; // ======================================== // Bring to the front and give focus // ======================================== procedure TWinApp.SetFocus; var Wnd: THandle; begin if FProcHandle <> 0 then begin Wnd := GetWindowHandle; if (Wnd <> 0) and GetAppRunning then begin SetForegroundWindow(Wnd); ShowWindow(Wnd, SW_RESTORE); end; end; end; end. |