🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: borland.public.cppbuilder.nativeapi
Article #4909

Re: Spawning and controlling

#4909
From: Michael Gillen
Date: Fri, 11 Nov 2005 17:58
570 lines
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-mail
References: <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