Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Run without Dos box
Message
From
01/12/1998 07:08:47
 
 
General information
Forum:
Visual FoxPro
Category:
FoxPro 2.x
Miscellaneous
Thread ID:
00139702
Message ID:
00162702
Views:
27
>>>>David,
>>>>
>>>>I pciked this up off of UT, it is not my work and I don't have the name of the original author. I wish I did. I believe I found it in the files section here on UT. I've played around with it and it seems to work.
>>>>
>>>
>>>That's one of mine; I've been playing around with a few refinements. The code here was posted in a FAQ a week or so ago.
>>
>>Ed,
>>
>>Did you post any updates to the DOS run procedure that you posted some time ago. I've been using your technique and would appreciate an update if one is available.
>
>For the masochists among you, the following is a class I've been playing with for a while; it's stable, modertely well commented, but I've yet to get the time to add all the finishing touches and write a help file describing its use. It works in all versions of VFP I've tested it with to date.
>
>
>**************************************************
>*-- Class:        api_apprun (e:\processclass\process.vcx)
>*-- ParentClass:  custom
>*-- BaseClass:    custom
>*
>DEFINE CLASS api_apprun AS custom
>
>
>	PROTECTED inprocesshandle
>	inprocesshandle = .NULL.
>	PROTECTED inthreadhandle
>	inthreadhandle = .NULL.
>	iccommandline = "space(0)"
>	iclaunchdir = (space(0))
>	icwindowmode = (space(0))
>	icerrormesage = (space(0))
>	Name = "api_apprun"
>	icerrormessage = .F.
>
>
>	PROCEDURE launchapp
>		LOCAL cCommandLine, uFromDir, cWindowMode
>		WITH This
>			.icErrorMessage = ''
>			IF TYPE('.icCommandLine') # 'C'
>				*	Command line must be a character string
>				.icErrorMessage = 'icCommandLine must be set, and a string value'
>				RETURN .F.
>			ELSE
>				cCommandLine = ALLTRIM(.icCommandLine)
>			ENDIF
>			IF TYPE('.icLaunchDir') # 'C' OR EMPTY(.icLaunchDir)
>				*	If not a character string, pass a null pointer, defaulting to Current Working Dir
>				uFromDir = 0
>			ELSE
>				*	Otherwise, null pad the string
>				uFromDir = .icLaunchDir + CHR(0)
>			ENDIF
>			IF TYPE('.icWindowMode') # 'C'
>				*	If not passed, set to null string
>				cWindowMode = ''
>			ELSE
>				*	Translate the passed window mode to uppercase
>				cWindowMode = UPPER(.icWindowMode)
>			ENDIF
>			*	This API call does the work.  The parameters are as follows:
>			*		lpszModuleName - ptr-> file name of module to execute.  Since we aren't launching .CPLs, do not use
>			*		lpszCommandLine - ptr-> command to execute, as passed in method
>			*		lpSecurityAttributesProcess - ptr-> SECURITY_ATTRIBUTES structure for Process.  Pass a null pointer
>			*		lpSecurityAttributesThread - ptr-> SECURITY_ATTRIBUTES structure for first thread.  Pass a null pointer
>			*		bInheritHandles - whether or not chlid inherits parent handles.  Since no SECURITY_ATTRIBUTES passed, default to FALSE
>			*		dwCreateFlags - Process Creation Mode flag set.  we use the default mode at normal priority, ie 0
>			*		lpvEnvironment	- ptr-> a set of environment strings as if a MULTI_SZ.  We don't set, so pass a null pointer
>			*		lpszStartupDir - ptr-> the starting directory.  If none provided to method, pass a null pointer
>			*		lpStartInfo - ptr-> a STARTUPINFO structure.  We use one structure member at times.
>			*		lpProcessInfo - ptr-> a PROCESS_INFORMATION structure, used to return PID/PHANDLE detail.  We use one member
>			DECLARE SHORT CreateProcess IN WIN32API AS CrPr ;
>				STRING lpszModuleName, ;
>				STRING @lpszCommandLine, ;
>				STRING lpSecurityAttributesProcess, ;
>				STRING lpSecurityAttributesThread, ;
>				SHORT bInheritHandles, ;
>				INTEGER dwCreateFlags, ;
>				STRING lpvEnvironment, ;
>				STRING lpszStartupDir, ;
>				STRING @lpStartInfo, ;
>				STRING @lpProcessInfo
>
>			LOCAL cProcessInfo, cStartUpInfo
>
>			*	Make default Structures for the CreateProcess call
>			*
>			*	ProcessInfo -	struc, 4 DWORDs, a Process Handle, a Thread Handle, a ProcessID and a ThreadID
>			*					we save the Process and Thread Handles in member properties to ensure that
>			*					they are properly disposed at Destroy by CloseHandle()
>
>			cProcessInfo = REPL(CHR(0),16)
>
>			*	StartUpInfo is a 68 byte long complex structure;  we either have 68 bytes with a cb member (byte 1) 68
>			*	or with cb of 68, dwFlag low order byte (byte 45) of 1, and low order byte wShowWindow (byte 49) set to
>			*	the SW_ value appropriate for the Window Mode desired.
>
>			DO CASE
>			CASE cWindowMode = 'HID'
>				*	Hide - use STARTF_USESHOWFLAG and value of 0
>				cStartUpInfo = CHR(68) + ;
>								REPL(CHR(0),43) + ;
>								CHR(1) + ;
>								REPL(CHR(0),23)
>			CASE cWindowMode = 'NOR'
>				*	Normal - use STARTF_USESHOWFLAG and value of 1
>				cStartUpInfo = CHR(68) + ;
>								REPL(CHR(0),43) + ;
>								CHR(1) + ;
>								REPL(CHR(0),3) + ;
>								CHR(1) + ;
>								REPL(CHR(0),19)
>			CASE cWindowMode = 'MIN'
>				*	Minimize - use STARTF_USESHOWFLAG and value of 2
>				cStartUpInfo = CHR(68) + ;
>								REPL(CHR(0),43) + ;
>								CHR(1) +  ;
>								REPL(CHR(0),3) + ;
>								CHR(2) + ;
>								REPL(CHR(0),19)
>			CASE cWindowMode = 'MAX'
>				*	Maximize - use STARTF_USESHOWFLAG and value of 3
>				cStartUpInfo = CHR(68) + ;
>								REPL(CHR(0),43) + ;
>								CHR(1) +  ;
>								REPL(CHR(0),3) + ;
>								CHR(3) + ;
>								REPL(CHR(0),19)
>			OTHERWISE
>				*	Use default of application
>				cStartUpInfo = CHR(68) + REPL(CHR(0),67)
>			ENDCASE
>			LOCAL lResult
>			lResult = CrPr(	0, ;
>							cCommandLine, ;
>							0, 0, 0, 0, 0, ;
>							uFromDir, ;
>							@cStartUpInfo, ;
>							@cProcessInfo)
>			*	Strip the handles from the PROCESS_INFORMATION structure and save in private properties
>			IF lResult = 1
>				.ParseProcessInfoStruc(cProcessInfo)
>				RETURN .T.
>			ELSE
>				.icErrorMessage = 'Process Specified by icCommandLine could not be started'
>				RETURN .F.
>			ENDIF
>		ENDWITH
>	ENDPROC
>
>
>	PROCEDURE launchappandwait
>		#DEFINE cnINFINITE 		0xFFFFFFFF
>		#DEFINE cnHalfASecond	500	&& milliseconds
>		#DEFINE cnTimedOut		258	&& 0x0102
>		*	We need some API calls, declare here
>		*	GetCurrentProcess returns the pseudohandle of the current process (ie VFP instance)
>		DECLARE INTEGER GetCurrentProcess IN WIN32API AS GetCurrProc
>		*	WaitForIdleInput waits until the application is instantiated and at it's event loop
>		DECLARE INTEGER WaitForInputIdle IN WIN32API AS WaitInpIdle ;
>			INTEGER nProcessHandle, ;
>			INTEGER nWaitForDuration
>		*	WaitForSingleObject waits until the handle in parm 1 is signalled or the timeout period expires
>		DECLARE INTEGER WaitForSingleObject IN WIN32API AS WaitOnAppExit ;
>			INTEGER hProcessHandle, ;
>			INTEGER dwTimeOut
>		*	Save the Process handle if any and the result of LaunchApp
>		*	Fire the app and save the process handle
>		LOCAL uResult
>		uResult = 0
>		WITH This
>			.icErrorMessage = ''
>			IF .LaunchApp()
>				uResult = 1
>				*	It's been launched;  wait until we're idling along
>				=WaitInpIdle(GetCurrProc(),cnINFINITE)
>				*	As long as the other process exists, wait for it
>				DO WHILE WaitOnAppExit(.inProcessHandle, cnHalfASecond) = cnTimedOut
>					*	Give us an out in case the other app hangs - let <Esc> terminate waits
>					IF INKEY() = 27
>						*	Still running but we aren't waiting - return a -1 as a warning
>						.icErrorMessage = 'Process started but user did not wait on termination'
>						uResult = 0
>						EXIT
>					ENDIF
>				ENDDO
>			ELSE
>				*	Return 0 to indicate failure
>				uResult = 0
>			ENDIF
>		ENDWITH
>		RETURN (uResult = 1)
>	ENDPROC
>
>
>	PROCEDURE checkprocessexitcode
>		LPARAMETER nProcessToCheck
>		IF TYPE('nProcessToCheck') # 'N'
>			nProcessToCheck = this.inProcessHandle
>		ENDIF
>		DECLARE SHORT GetExitCodeProcess IN Win32API AS CheckExitCode ;
>			INTEGER hProcess, ;
>			INTEGER @lpdwExitCode
>		LOCAL nExitCode
>		nExitCode = 0
>		IF ! ISNULL(nProcessToCheck)
>			IF CheckExitCode(nProcessToCheck, @nExitCode) = 1
>				*	We retrieved an exit code (259 means still running, tho
>				RETURN nExitCode
>			ELSE
>				*	Process did not exist in process table - no exit status
>				this.icErrorMessage = 'Process to check not in active Process Table'
>				RETURN NULL
>			ENDIF
>		ELSE
>			this.icErrorMessage = 'NULL process handle passed to CheckProcessExitCode()'
>			RETURN NULL
>		ENDIF
>	ENDPROC
>	PROTECTED PROCEDURE releasehandle
>		LPARAMETER nHandleToRelease
>		LOCAL nResult
>		*	Use CloseHandle(), returns a BOOL;  0 = False
>		DECLARE SHORT CloseHandle IN Win32API AS CloseHand INTEGER nHandleToClose
>		IF TYPE('nHandleToRelease') = 'N' AND ! ISNULL(nHandleToRelease)
>			nResult = CloseHand(nHandleToRelease)
>			this.icErrorMessage = IIF(nResult = 0, 'CloseHandle() failed to close handle','')
>		ELSE
>			this.icErrorMessage = 'Invalid handle passed to ReleaseHandle() invocation'
>			nResult = 0
>		ENDIF
>		RETURN (nResult = 1)
>	ENDPROC
>
>
>	PROCEDURE getprochandle
>		RETURN this.inProcessHandle
>	ENDPROC
>
>
>	PROCEDURE killproc
>		LPARAMETER nProcessToKill
>		IF TYPE('nProcessToKill') # 'N'
>			nProcessToKill = This.inProcessHandle
>		ENDIF
>		DECLARE SHORT TerminateProcess IN WIN32API AS KillProc ;
>			INTEGER hProcess, ;
>			INTEGER uExitCode
>		LOCAL nResult
>		IF ! ISNULL(nProcessToKill)
>			nResult = KillProc(nProcessToKill,0)
>			this.icErrorMessage = IIF(nResult = 0, 'TerminateProcess() could not kill process handle requested','')
>		ELSE
>			this.icErrorMessage = 'NULL handle passed to KillProc()'
>			nResult = 0
>		ENDIF
>		RETURN (nResult = 1)
>	ENDPROC
>
>
>	PROCEDURE parseprocessinfostruc
>		LPARAMETER cProcessInfoStructure
>		WITH This
>			.inProcessHandle = .ExtractDWORD(cProcessInfoStructure)
>			.inThreadHandle = .ExtractDWORD(SUBST(cProcessInfoStructure,5))
>		ENDWITH
>
>	ENDPROC
>
>
>	PROCEDURE extractdword
>		LPARAMETER cStringToExtractFrom
>		IF TYPE('cStringToExtractFrom')='C' AND LEN(cStringToExtractFrom) >= 4
>			RETURN (((ASC(SUBST(cStringToExtractFrom,4,1))*256) + ;
>									ASC(SUBST(cStringToExtractFrom,3,1)))*256 + ;
>									ASC(SUBST(cStringToExtractFrom,2,1)))*256 + ;
>									ASC(LEFT(cStringToExtractFrom,1))
>		ELSE
>			this.icErrorMessage = 'Invalid DWORD string passed for conversion'
>			RETURN NULL
>		ENDIF
>	ENDPROC
>
>
>	PROCEDURE Destroy
>		WITH THIS
>			IF TYPE('.inThreadHandle') = 'N' AND NOT ISNULL(.inThreadHandle)
>				.ReleaseHandle(.inThreadHandle)
>				.inThreadHandle = NULL
>			ENDIF
>			IF TYPE('.inProcessHandle') = 'N' AND NOT ISNULL(.inProcessHandle)
>				.ReleaseHandle(.inProcessHandle)
>				.inProcessHandle = NULL
>			ENDIF
>		ENDWITH
>		DODEFAULT()
>
>
>
>	ENDPROC
>
>
>	PROCEDURE Init
>		*
>		*	API_AppRun - use the CreateProcess() API to launch, monitor, and kill an Executable
>		*
>		*	Properties:
>		*
>		*	inProcessHandle			(P)	ProcessHandle generated by CreateProcess()
>		*	inThreadHandle			(P) ThreadHandle for First Thread of inProcessHandle
>		*	icErrorMessage			R/O Error Message Detailed Description
>		*	icCommandLine			R/W Command Line to launch via CreateProcess()
>		*	icLaunchDir				R/W Directory to use as startup dir for CreateProcess()
>		*	icWindowMode			R/W Window Start Mode, one of (HID, NOR, MIN, MAX) or empty
>		*							defaults to empty, the default for the executable is used
>		*
>		*	Methods:
>		*
>		*	Init					(O) Command Line, (O) Start Dir, (O) Window Start Mode
>		*							If sent, the icCommandLine, icLaunchDir and icWindowMode properties are set
>		*	Destroy
>		*	LaunchApp				// Launches .icComandLine from .icLaunchDir in .icWindowMode
>		*							// NB - at least .icCommandLine must be set to not fail
>		*							RETURNS: BOOL, check icErrorMessage on .F.
>		*	LaunchAppAndWait		// Call LaunchApp() and wait on either user termination or process termination
>		*							RETURNS: BOOL, check icErrorMessage on .F.
>		*	CheckProcessExitCode	(O) Process handle to check, defaults to .inProcessHandle
>		*							// Get Process named by Process Handle's Exit Code (259 = still running)
>		*							RETURNS:  Integer, check on NULL, if NULL, check icErrorMessage
>		*	ExtractDWORD			(R) String to convert
>		*							//Converts a 4 byte or longer string to a DWORD integer
>		*							RETURNS:  Integer, check on NULL, if NULL arg was invalid
>		*	KillProc				(O) Process handle to Terminate, defaults to .inProcessHandle
>		*							// Kills specified process using TerminateProcess()
>		*							RETURNS:  BOOL, check icErrorMessage on .F.
>		*	GetProcHandle			//  Returns the Process Handle for the current Process
>		*							// NB - only useful for KillProc(), since Destroy will close the handle
>		*							RETURNS:  Integer, check for NULL, if NULL no process was started yet
>		*	ParsePROCESSINFOStruc	// Pulls the Process Handle and Thread Handle from the PROCESSINFO structure
>		*							// Only used internally
>		*	ReleaseHandle			(R)  Handle to Close
>		*							//  Invokes CloseHandle() to explicitly release process/thread handles
>		*							//  Only used internally, but can be externalized
>		*							RETURNS:  BOOL, check .icErrorMessage on .F.
>		*
>		LPARAMETERS tcCommandLine, tcLaunchDir, tcWindowMode
>		*	Set up the environment for the object
>		LOCAL aDirTest[1,5]
>		WITH THIS
>			.icErrorMessage = ''
>			.icCommandLine = ''
>			.icLaunchDir = ''
>			.icWindowMode = ''
>			.inProcessHandle = NULL
>			.inThreadHandle = NULL
>			* store parameters if passed
>			IF TYPE('tcCommandLine') = 'C'
>				.icCommandLine = ALLTRIM(tcCommandLine)
>			ENDIF
>			DO CASE
>			CASE TYPE('tcLaunchDir') # 'C'
>				*	Not a character expression - ignore
>			CASE ADIR(aDirTest, tcLaunchDir, 'D') # 1
>				*	Either directory doesn't exist, or there's a wildcard in the expression
>				.icErrorMessage = 'Invalid directory for startup passed to Init method'
>			OTHERWISE
>				*	Valid directory - save it
>				.icLaunchDir = ALLTRIM(tcLaunchDir)
>			ENDCASE
>			DO CASE
>			CASE TYPE('tcWindowMode') # 'C'
>				*	Not passed in or not valid type
>			CASE INLIST(PADR(UPPER(ALLTRIM(tcWindowMode)),3),'NOR','MIN','MAX','HID')
>				*	Valid mode - set it
>				.icWindowMode = PADR(UPPER(ALLTRIM(tcWindowMode)),3)
>			OTHERWISE
>				*	No a valid character string
>				IF ! EMPTY(.icErrorMessage)
>					.icErrorMessage = .icErrorMessage + ' &' + CHR(13) + CHR(10)
>				ENDIF
>				.icErrorMessage = .icErrorMessage + 'Invalid WindowMode passed to Init Method'
>			ENDCASE
>		ENDWITH
>		RETURN .T.
>	ENDPROC
>
>
>ENDDEFINE
>*
>*-- EndDefine: api_apprun
>**************************************************

I hadn't included any examples of how it gets used:

SET PROCEDURE TO Process  && Assumes the above class is in PROCESS.PRG
oProcess = CREATEOBJ('API_AppRun','NOTEPAD.EXE AUTOEXEC.BAT','C:\','NOR')
*Run the application and don't wait to terminate
oProcess.LaunchApp()
*Check the exit status;  259 means still running
IF oProcess.CheckProcessExitCode() = 259
	wait window 'Still running'
ELSE
	wait window 'Terminated with a '+alltrim(str(oProcess.CheckProcessExitCode()))
ENDIF
*Make it go away no matter what
? oProcess.KillProc()
oProcess = ''
*  You can have multiple processes running at once
oProcess1 = CREATEOBJ('API_AppRun','REGEDIT','C:\','NOR')
oProcess2 = CREATEOBJ('API_AppRun','NET USE /? | MORE')
oProcess1.LaunchApp()
oProcess2.LaunchApp()
oProcess1 = ''
oProcess2 = ''
* Run them both and wait on the last to terminate
oProcess1 = CREATEOBJ('API_AppRun','NOTEPAD.EXE AUTOEXEC.BA','C:\','NOR')
oProcess2 = CREATEOBJ('API_AppRun','NOTEPAD.EXE CONFIG.SY','C:\','MIN')
oProcess1.LaunchApp()
oProcess2.LaunchAppAndWait()
EMail: EdR@edrauh.com
"See, the sun is going down..."
"No, the horizon is moving up!"
- Firesign Theater


NT and Win2K FAQ .. cWashington WSH/ADSI/WMI site
MS WSH site ........... WSH FAQ Site
Wrox Press .............. Win32 Scripting Journal
eSolutions Services, LLC

The Surgeon General has determined that prolonged exposure to the Windows Script Host may be addictive to laboratory mice and codemonkeys
Previous
Reply
Map
View

Click here to load this message in the networking platform