Mirror

Class to Execute and Manage External EXE in your App (Views: 714)

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.


<< Back to main page