Article View: borland.public.cppbuilder.nativeapi
Article #4909Re: Spawning and controlling
From: Michael Gillen
Date: Fri, 11 Nov 2005 17:58
Date: Fri, 11 Nov 2005 17:58
570 lines
16673 bytes
16673 bytes
Denville Longhurst wrote:
> Hi
>
> From within my application, I would like to spawn an arbitrary third party application (say
> foo.exe) in the following way:
>
> 1. Check whether foo.exe is already running so that I don't attempt to run it twice;
> 2. Spawn foo.exe
> 3. Make foo's parent my application, so that it displays within one of my windows;
> 4. From my app, terminate foo.exe
>
> I can probably cope with (3), but I am at a loss about how to do the rest.
This bunch of code should give you the needed tools. This requires Win2000 or better, however,
since I never implemented backwards compatability.
/*********** PROCCONTROL.H *************/
#ifndef ProcControlH
#define ProcControlH
#include <vcl.h>
#include <Wbemcli.h>
#include <tlhelp32.h>
#define TA_FAILED 0
#define TA_SUCCESS_CLEAN 1
#define TA_SUCCESS_KILL 2
#define TA_SUCCESS_16 3
int __fastcall TerminateAppByName(AnsiString ExeName, int timeout = 2000);
bool __fastcall IsProcessRunning(AnsiString ExeName);
AnsiString __fastcall GetProcessFilePath(AnsiString ExeName);
HWND WINAPI WindowFromFileName(AnsiString ExeName);
void __fastcall LaunchApplication(AnsiString ExeName, bool wait = false);
//---------------------------------------------------------------------------
#endif
/*********** PROCCONTROL.CPP *************/
#include "ProcControl.h"
#include "utilcls.h"
IWbemLocator *pLoc;
IWbemServices *pSvc;
IEnumWbemClassObject *pEnum;
IWbemClassObject *pProcesses;
typedef struct
{
DWORD dwID ;
DWORD dwThread ;
} TERMINFO ;
// Declare Callback Enum Functions.
BOOL CALLBACK TerminateAppEnum( HWND hwnd, LPARAM lParam ) ;
BOOL CALLBACK Terminate16AppEnum( HWND hwnd, LPARAM lParam ) ;
DWORD WINAPI TerminateApp( DWORD dwPID, DWORD dwTimeout ) ;
DWORD WINAPI Terminate16App( DWORD dwPID, DWORD dwThread, WORD w16Task, DWORD dwTimeout );
//---------------------------------------------------------------------------
// IsProgramRunning(AnsiString exefile)
// Determine if program 'exefile' is running.
//---------------------------------------------------------------------------
bool __fastcall IsProcessRunning(AnsiString ExeName)
{
HANDLE hSnap;
PROCESSENTRY32 process;
BOOL f;
hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if ( hSnap == NULL )
return false; // NOT RUNNING!!
process.dwSize = sizeof(PROCESSENTRY32);
f = Process32First(hSnap, &process);
while ( f )
{
AnsiString file(ExtractFileName(process.szExeFile));
if ( file.AnsiCompareIC(ExeName) == 0 )
{
CloseHandle(hSnap);
return true; // PROGRAM IS RUNNING!
}
f = Process32Next(hSnap, &process);
}
CloseHandle(hSnap);
return false; // NOT RUNNING!!
}
//---------------------------------------------------------------------------
// IsProgramRunning(AnsiString exefile)
// Determine if program 'exefile' is running.
// Return the full path to the executable if it IS running, else NULL.
//---------------------------------------------------------------------------
int __fastcall TerminateAppByName(AnsiString ExeName, int timeout)
{
DWORD dwID = 0;
GetWindowThreadProcessId(WindowFromFileName(ExeName), &dwID) ;
int ret = TerminateApp( dwID, timeout );
return ret;
/*
if(ret == TA_FAILED)
OutputDebugString("The shutdown failed");
if(ret == TA_SUCCESS_CLEAN)
OutputDebugString("The process was shutdown using WM_CLOSE.");
if(ret == TA_SUCCESS_KILL)
OutputDebugString("The process was shut down with TerminateProcess().");
*/
}
//---------------------------------------------------------------------------
bool __fastcall InitCom()
{
HRESULT hres;
hres = CoInitializeEx(0, COINIT_MULTITHREADED); //Initialize COM.
if ( FAILED(hres) )
{
ShowMessage("Failed to initialize COM library. Error code = "+IntToHex((int)hres,8));
return false;
}
hres = CoInitializeSecurity(
NULL, // security descriptor
-1, // use this simple setting
NULL, // use this simple setting
NULL, // reserved
RPC_C_AUTHN_LEVEL_CONNECT, // Windows NT 4/Windows 2000 authentication level
RPC_C_IMP_LEVEL_IMPERSONATE, // impersonation level
NULL, // use this simple setting
EOAC_NONE, // no special capabilities
0); // reserved
if ( FAILED(hres) )
{
ShowMessage("Failed to initialize security. Error code = "+IntToHex((int)hres,8));
CoUninitialize();
return false;
}
return true;
}
//---------------------------------------------------------------------------
AnsiString __fastcall GetProcessFilePath(AnsiString ExeName)
{
AnsiString ExePath = NULL;
HRESULT hres;
pLoc = 0;
//IWbemLocator interface for obtaining the initial namespace pointer to
//the Windows Management on a particular host computer.
hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)
&pLoc);
if ( FAILED(hres) )
{
ShowMessage("Failed to create IWbemLocator object. Err code = "+IntToHex((int)hres,8));
CoUninitialize();
return NULL;
}
pSvc = 0;
hres = pLoc->ConnectServer(L"\\\\.\\root\\cimv2", NULL, NULL, 0, NULL, 0, 0, &pSvc);
if ( FAILED(hres) )
{
ShowMessage("Could not connect. Error code = "+IntToHex((int)hres,8));
CoUninitialize();
return NULL;
}
// Set the proxy so that impersonation of the client occurs.
hres = CoSetProxyBlanket(pSvc,
RPC_C_AUTHN_WINNT, // NTLM authentication service
RPC_C_AUTHZ_NONE, // default authorization service...
NULL, // no mutual authentication
RPC_C_AUTHN_LEVEL_CONNECT, // authentication level
RPC_C_IMP_LEVEL_IMPERSONATE, // impersonation level
NULL, // use current token
EOAC_NONE); // no special capabilities
if ( FAILED(hres) )
{
ShowMessage("Could not set proxy blanket. Error code = "+IntToHex((int)hres,8));
pSvc->Release();
pLoc->Release();
CoUninitialize();
return NULL;
}
BSTR className = SysAllocString(L"Win32_Process");
hres = pSvc->GetObject(className, 0, NULL, &pProcesses, NULL );
//Retrieves an class object.
ULONG puReturned;
//Create instance for enumeration ports
hres=pSvc->CreateInstanceEnum( className, NULL, NULL, &pEnum );
//no need in variable className now.
SysFreeString(className);
if ( hres != 0 )
{
ShowMessage("Error");
return NULL;
}
// Retrieve the objects in the result set.
for ( ;; )
{
ULONG uReturned = 0;
hres = pEnum->Next(
WBEM_INFINITE, // Time out
1, // One object
&pProcesses,
&uReturned
);
if ( uReturned == 0 )
{
break;
}
IWbemClassObject *pObj1;
VARIANT Val;
VARIANT Val1;
VARIANT Val2;
VARIANT Val3;
VariantInit(&Val);
VariantInit(&Val1);
VariantInit(&Val2);
VariantInit(&Val3);
CIMTYPE pvtType;
BSTR Name1 = SysAllocString(L"ExecutablePath");
BSTR Name2 = SysAllocString(L"Name");
BSTR Name3 = SysAllocString(L"Handle");
LPSTR PathToProcess1=0;
LPSTR NameProc1=0;
LPSTR Handle1=0;
hres=pProcesses->Get(Name1,0,&Val1,&pvtType,NULL);
if ( Val1.vt == VT_NULL )
PathToProcess1="UNKNOWN";
else
PathToProcess1=WideToAnsi(Val1.bstrVal);
hres=pProcesses->Get(Name2,0,&Val2,&pvtType,NULL);
if ( Val2.vt == VT_NULL )
NameProc1="UNKNOWN";
else
NameProc1=WideToAnsi(Val2.bstrVal);
hres=pProcesses->Get(Name3,0,&Val3,&pvtType,NULL);
if ( Val3.vt == VT_NULL )
continue;
else
Handle1=WideToAnsi(Val3.bstrVal);
SysFreeString(Name1);
SysFreeString(Name2);
SysFreeString(Name3);
VariantClear(&Val);
VariantClear(&Val1);
VariantClear(&Val2);
VariantClear(&Val3);
if ( !stricmp(NameProc1, ExeName.c_str()) )
ExePath = PathToProcess1;
if ( Handle1 )
delete []Handle1;
if ( PathToProcess1 && (AnsiString(PathToProcess1) != "UNKNOWN") )
delete []PathToProcess1;
if ( NameProc1 )
delete []NameProc1;
}
return ExePath;
}
/*-------------------------------------------------------------
BaseFileNamePtr(const char *full_name)
Get the address of the base file name
from a string which may contain a path
Returns a pointer to the base file name
Calling parameters
full_name the name of the file
Win API functions used
none
Other functions used
strrchr
Globals and constants used
none
-------------------------------------------------------------*/
static char *WINAPI BaseFileNamePtr(const char *full_name)
{
char *p = strrchr((char *) full_name, '\\');
if ( p )
{
++p;
} else
{
p = (char *) full_name;
}
return p;
}
//---------------------------------------------------------------------------
//-------------------------------------------------------------
// ProcIDOrHwnd is a union used for communication
// with the Enum Windows procedure
//-------------------------------------------------------------
union ProcIDOrHwnd
{
DWORD proc_id;
HWND hwnd;
};
/*-------------------------------------------------------------
EWProc(HWND hwnd, LPARAM l)
Service procedure for an EnumWindows call
Compare the process id for the owner of the
window to that passed by the caller and
if they match, save the window handle in the
union whose address is the calling parameter
Calling parameters
hwnd handle of a window that was found
by the EnumWindows function
l address of a ProcIDOrHwnd union
which contains the process id of the
window to be found
On return the union's hwnd member will be the
handle of the found window. If none was found
then it will be the process id.
Win API functions used
GetWindowThreadProcessId
Other functions used
none
Globals and constants used
none
-------------------------------------------------------------*/
static BOOL EWProc(HWND hwnd, LPARAM l)
{
DWORD proc_id;
ProcIDOrHwnd *pih = reinterpret_cast<ProcIDOrHwnd*>(l);
GetWindowThreadProcessId(hwnd, &proc_id);
if ( proc_id == pih->proc_id )
{
pih->hwnd = hwnd;
return FALSE;
}
return TRUE;
}
/*---------------------------------------------------------------------------
WindowFromFile(const char *file_name)
Find a window which was created by a program
whose file name is given
Calling arguments
file_name The name of the file. It may
contain a path, but the path
part is not used.
Returns the HWND of the window which was found
or NULL if no window was found
Windows API functions used
CreateToolhelp32Snapshot
Process32First
Process32Next
CloseHandle
EnumWindows
Other functions used
BaseFileNamePtr
stricmp
Globals and constants used
none
-------------------------------------------------------------*/
HWND WINAPI WindowFromFileName(AnsiString ExeName)
{
HWND found_hwnd = NULL;
HANDLE snap_hand =
CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if ( (int) snap_hand != -1 )
{
DWORD proc_id = 0;
PROCESSENTRY32 pe32;
char *base_file_name = BaseFileNamePtr(ExeName.c_str());
OutputDebugString(base_file_name);
pe32.dwSize = sizeof(pe32);
// cycle through each process
if ( Process32First(snap_hand, &pe32) )
{
do
{
char *base_name = BaseFileNamePtr(pe32.szExeFile);
// if the process' file name is the same capture
// the process id and break out of the loop
if ( !stricmp(base_file_name, base_name) )
{
proc_id = pe32.th32ProcessID;
break;
}
} while ( Process32Next(snap_hand, &pe32) );
CloseHandle(snap_hand);
}
// if a process id was found
if ( proc_id )
{
ProcIDOrHwnd pih;
pih.proc_id = proc_id;
// find a window owned by that process
EnumWindows((WNDENUMPROC)(EWProc), reinterpret_cast<LPARAM>(&pih));
// if a window was found
if ( pih.proc_id != proc_id )
{
found_hwnd = pih.hwnd; // save the window handle to return to caller
}
}
}
return found_hwnd;
}
//---------------------------------------------------------------------------
void __fastcall LaunchApplication(AnsiString ExeName, bool wait)
{
String CmdLine = ExeName;
if ( !FileExists(CmdLine) )
{
MessageDlg(CmdLine +AnsiString(" does not exist!"), mtError, TMsgDlgButtons() << mbOK, 0);
return;
}
Application->ProcessMessages();
// initialize STARTUPINFO struct
STARTUPINFO si;
memset(&si, 0, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWNORMAL;
PROCESS_INFORMATION ProcessInfo;
if ( CreateProcess
(
NULL, // pointer to name of executable module
CmdLine.c_str(), // pointer to command line string
NULL, // pointer to process security attributes
NULL, // pointer to thread security attributes
true, // handle inheritance flag
NORMAL_PRIORITY_CLASS, // creation flags
NULL, // pointer to new environment block
ExtractFilePath(Application->ExeName).c_str(), // pointer to current directory name
&si, // pointer to STARTUPINFO
&ProcessInfo // pointer to PROCESS_INFORMATION
) )
{ // body of the if...
if ( wait )
WaitForSingleObject(ProcessInfo.hProcess, 20000); // Wait for it to be ready
Application->ProcessMessages();
CloseHandle(ProcessInfo.hProcess); // done with these
CloseHandle(ProcessInfo.hThread);
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
BOOL CALLBACK TerminateAppEnum( HWND hwnd, LPARAM lParam )
{
DWORD dwID ;
GetWindowThreadProcessId(hwnd, &dwID) ;
if ( dwID == (DWORD)lParam )
{
PostMessage(hwnd, WM_CLOSE, 0, 0) ;
}
return TRUE ;
}
/*----------------------------------------------------------------
DWORD WINAPI TerminateApp( DWORD dwPID, DWORD dwTimeout )
Purpose: Shut down a 32-Bit Process (or 16-bit process under Win95)
Parameters: dwPID Process ID of the process to shut down.
dwTimeout Wait time in milliseconds before shutting down process.
Return Value:
TA_FAILED - If the shutdown failed.
TA_SUCCESS_CLEAN - If the process was shutdown using WM_CLOSE.
TA_SUCCESS_KILL - if the process was shut down with TerminateProcess().
NOTE: See header for these defines.
---------------------------------------------------------------------------*/
DWORD WINAPI TerminateApp( DWORD dwPID, DWORD dwTimeout )
{
HANDLE hProc ;
DWORD dwRet ;
// If we can't open the process with PROCESS_TERMINATE rights,
// then we give up immediately.
hProc = OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, dwPID);
if ( hProc == NULL )
{
return TA_FAILED ;
}
// TerminateAppEnum() posts WM_CLOSE to all windows whose PID
// matches your process's.
EnumWindows((WNDENUMPROC)TerminateAppEnum, (LPARAM) dwPID) ;
// Wait on the handle. If it signals, great. If it times out,
// then you kill it.
if ( WaitForSingleObject(hProc, dwTimeout)!=WAIT_OBJECT_0 )
dwRet=(TerminateProcess(hProc,0)?TA_SUCCESS_KILL:TA_FAILED);
else
dwRet = TA_SUCCESS_CLEAN ;
CloseHandle(hProc);
return dwRet ;
}
//---------------------------------------------------------------------------
--
-Michael Gillen
Technical Details
Message-ID:
<4374e9ea@newsgroups.borland.com>Path:
arc1-ca.pugleaf.net!g2news1.google.com!news1.google.com!proxad.net!gatel-ffm!gatel-ffm!194.25.134.126.MISMATCH!newsfeed01.sul.t-online.de!t-online.de!newsgroups.borland.com!not-for-mailReferences:
<4374b248$1@newsgroups.borland.com>Show Full Headers
Complete Article Headers
X-Google-Thread: 10cc67,d85ed3b722a8f188 X-Google-Attributes: gid10cc67,public X-Google-Language: ENGLISH,ASCII-7-bit From: "Michael Gillen" <michael@gillenconsultinggroup-nospam.com> Subject: Re: Spawning and controlling Newsgroups: borland.public.cppbuilder.nativeapi References: <4374b248$1@newsgroups.borland.com> User-Agent: XanaNews/1.17.6.6 NNTP-Posting-Host: 67.122.199.249 Message-ID: <4374e9ea@newsgroups.borland.com> Date: 11 Nov 2005 10:58:50 -0700 X-Trace: newsgroups.borland.com 1131735530 67.122.199.249 (11 Nov 2005 10:58:50 -0700) Lines: 570 Path: arc1-ca.pugleaf.net!g2news1.google.com!news1.google.com!proxad.net!gatel-ffm!gatel-ffm!194.25.134.126.MISMATCH!newsfeed01.sul.t-online.de!t-online.de!newsgroups.borland.com!not-for-mail