Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Run an EXE and wait
Message
From
22/04/2019 08:48:12
Cetin Basoz
Engineerica Inc.
Izmir, Turkey
 
General information
Forum:
Visual FoxPro
Category:
Coding, syntax & commands
Miscellaneous
Thread ID:
01667388
Message ID:
01668224
Views:
95
Likes (1)
>Thought I could do this but cannot remember
>Want to run a c# .Exe from within VFP, but wait until the .exe has finished before running next VFP line of code
>Is there a simple way to do this ?

AFAIK there is no reliable way to do this from within VFP, using win32API or alike. I tried WaitForSingleObject and it was not reliable (a typical test would be against word and excel, whose behaviors are different). I had to trace not only the executables that are run from within VFP, but launched outside VFP as well (before .Net existed). My solution was to get a list of all running processes in intervals and compare what is not there anymore or new. Based on polling interval, I succeeded to get the new and finished processes with some negligible offset (ie: interval is 1 second means I would learn it at max 1 sec. after the process has finished).

I am adding the sample code to get apps running into a cursor and the needed fll that I created is attached. You might try running this AppLister.prg, then closing and\or running some executable, and then rerunning (changing the myCursor maybe to myCursor2) to compare results. You would see finished exe's as gone, and newly started ones as added to the cursor. HTH
*** AppLister.prg

#Define SW_HIDE             0
#Define SW_SHOWNORMAL       1
#Define SW_SHOWMINIMIZED    2
#Define SW_SHOWMAXIMIZED    3

#Define TH32CS_SNAPHEAPLIST 0x00000001
#Define TH32CS_SNAPPROCESS  0x00000002
#Define TH32CS_SNAPTHREAD   0x00000004
#Define TH32CS_SNAPMODULE   0x00000008
#Define TH32CS_SNAPALL      0x0000000F
#Define TH32CS_INHERIT      0x80000000

#Define MAX_MODULE_NAME32 255
#Define MAX_PATH          260
#Define SIZEOF_PE32 296
#Define SIZEOF_ME32 548

#Define WM_CLOSE				0x0010
#Define WM_QUERYENDSESSION		0x0011
#Define WM_QUIT					0x0012

** Sample usage
lnOldBehavior = SET("EngineBehavior")
Set EngineBehavior 70
oAppsLister = Newobject('AppsLister','appLister.prg')
oAppsLister.GetAppsRunning('MyCursor')
SET ENGINEBEHAVIOR m.lnOldBehavior
Select myCursor
Browse for !Empty(exeName)

Define Class AppsLister As Line
	Dimension arrDLLs[1]
	Me32Structure = ''
	Pe32Structure = ''
	ProcListCursor   = Sys(2015)
	ModuleListCursor = Sys(2015)
	AppsListCursor   = Sys(2015)

	Procedure Init
		Local Array arrDLLs[1]
		Adlls(arrDLLs) && Save defined DLLs
		Acopy(arrDLLs,This.arrDLLs)
		This.InitDLLs() && Declare DLLs
		This.Me32Structure = This.Int2Str(SIZEOF_ME32) + ;
			Replicate(Chr(0), SIZEOF_ME32-4)
		This.Pe32Structure = This.Int2Str(SIZEOF_PE32) + ;
			Replicate(Chr(0), SIZEOF_PE32-4)
		This.CreateCursors()
	Endproc

	Procedure CreateCursors
		Create Cursor (This.ModuleListCursor) ;
			(ProcessID i, szModule m, szExeFile m)
		Create Cursor (This.ProcListCursor) ;
			(ProcessID i,;
			cntThreads i,;
			parentPID i,;
			szExeFile m)
		Create Cursor (This.AppsListCursor) ;
			(wHwnd i,Pid i, Tid i,ClassName c(100),wCaption c(200))
	Endproc

	Procedure RemoveCursors
		Use In (This.ModuleListCursor)
		Use In (This.ProcListCursor)
		Use In (This.AppsListCursor)
	Endproc

	Procedure GetDesktopWindows2Array
		Lparameters taArray
		tcCursorName = Iif(Empty(m.tcCursorName), 'crsDesktop', m.tcCursorName)
		Local tempHwnd
		Create Cursor (m.tcCursorName) (nHwnd i)
		tempHwnd = GetActiveWindow() && Start from active
		Do While !Empty(tempHwnd)
			If IsWindowVisible(tempHwnd) = 1 And GetWindow(tempHwnd, GW_OWNER) = 0
				Insert Into (m.tcCursorName) Values (tempHwnd)
			Endif
			tempHwnd = GetWindow(tempHwnd, GW_HWNDNEXT)
		Enddo
		Select nHwnd From (m.tcCursorName) Into Array taArray
		Use In (m.tcCursorName)
	Endproc

	Procedure GetAppsRunning
		Lparameters tcCursorName, tlDestroyOldCursor
		If m.tlDestroyOldCursor
			This.CreateCursors()
		Endif
		Set Library To
		Set Library To "AppLauncher.fll" Additive
		DeskTopWindows2Array('arrDesktop')
		Set Library To

		For Each m.nHwnd In arrDesktop
			Store Replicate(Chr(0),200) To lpClassName, ctitle_bar
			lpdwProcessId = 0
			lnThreadID=GetWindowThreadProcessId(m.nHwnd, @lpdwProcessId)
			nBufLen = GetClassName(m.nHwnd,@lpClassName,200)
			rettext = GetWindowText(m.nHwnd, @ctitle_bar, 200)
			Insert Into (This.AppsListCursor) ;
				VALUES (m.nHwnd,m.lpdwProcessId,m.lnThreadID,;
				substr(m.lpClassName,1,m.nBufLen), ;
				LEFT(m.ctitle_bar, m.rettext))
		Endfor
		This.GetProcessList()
		Select ap.*,;
			pl.cntThreads, pl.parentPID, ;
			Nvl(ml.szModule,pl.szExeFile) As 'ModuleName', ;
			Nvl(ml.szExeFile,'') As exename ;
			from (This.AppsListCursor) ap ;
			INNER Join (This.ProcListCursor) pl On pl.ProcessID = ap.Pid ;
			left Join (This.ModuleListCursor) ml On pl.ProcessID = ml.ProcessID ;
			into Cursor __applst__ ;
			nofilter
		Select * From __applst__ ;
			union ;
			Select * From __applst__ ;
			into Cursor (m.tcCursorName) ;
			nofilter
		Use In '__applst__'
	Endproc

	Procedure GetModulesRunning
		Lparameters tcCursorName
		tcCursorName = Iif(Empty(m.tcCursorName),'Modules',m.tcCursorName)
		This.GetProcessList()

		Select pl.ProcessID, pl.cntThreads, pl.parentPID, ;
			Nvl(ml.szModule,pl.szExeFile) As 'ModuleName', ;
			Nvl(ml.szExeFile,'') As exename ;
			from (This.ProcListCursor) pl ;
			left Join (This.ModuleListCursor) ml On pl.ProcessID = ml.ProcessID ;
			into Cursor (m.tcCursorName) ;
			nofilter
	Endproc

	Procedure InitDLLs && Declare DLLs
		Declare Sleep In WIN32API Integer dwMillis
		Declare Integer GetActiveWindow In Win32API
		Declare short IsWindowVisible In WIN32API Integer HWnd
		Declare Integer GetWindow In Win32API;
			INTEGER HWnd, Integer dflag
		Declare Integer GetWindowText In Win32API ;
			INTEGER HWnd, String @lptstr, Integer cbmax
		Declare Integer GetClassName In WIN32API ;
			Integer HWnd, String @cClass, Integer nMaxBuffer
		Declare Integer GetWindowThreadProcessId In win32API ;
			Integer HWnd, Integer @lpdwProcessId
		Declare Integer CloseHandle In win32API Integer hObject
		Declare Integer CreateToolhelp32Snapshot In win32API ;
			INTEGER dwFlags, Integer th32ProcessID
		Declare Integer Process32First In win32API ;
			INTEGER hSnapshot, String @ lppe
		Declare Integer Process32Next In win32API ;
			INTEGER hSnapshot, String @ lppe
		Declare Integer Module32First In win32API ;
			INTEGER hSnapshot, String @ lpme
		Declare Integer Module32Next In win32API ;
			INTEGER hSnapshot, String @ lpme
		Declare RtlMoveMemory In WIN32API ;
			INTEGER @DestNumeric, ;
			STRING @pVoidSource, ;
			INTEGER nLength
		Declare Integer SendMessage In user32.Dll ;
			integer HWnd,;
			integer Msg, ;
			integer wParam, ;
			integer Lparam
		Declare Integer PostMessage In user32.Dll ;
			integer HWnd,;
			integer Msg, ;
			integer wParam, ;
			integer Lparam

	Endproc

	Procedure Destroy
		Local lnDLLs
		Local Array arrDLLs[1]
		lnDLLs = Adlls(arrDLLs) && Get current DLLs
		For ix=1 To m.lnDLLs
			If Empty(This.arrDLLs) Or ;
					Ascan(This.arrDLLs,arrDLLs[m.ix,2],1,-1,2,1+2+4+8) = 0
				Clear Dlls &arrDLLs[m.ix,2]
			Endif
		Endfor
		This.RemoveCursors()
	Endproc

	Procedure GetProcessModule
		Lparameters tnPID
		Local hModuleSnap, me32
		hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, m.tnPID)
		If hModuleSnap < 0
			Return .F.
		Endif
		me32 = This.Me32Structure
		lSuccess = ( Module32First(hModuleSnap, @me32) # 0)
		This.MODULEENTRY32_ToCursor(m.me32)
		CloseHandle (hModuleSnap)
	Endproc

	Function GetProcessList
		Local hProcessSnap, pe32, lSuccess
		hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
		pe32 = This.Pe32Structure
		*  Walk the snapshot of the processes
		*  and for each process, get module info
		lSuccess = ( Process32First(hProcessSnap, @pe32) # 0 )
		Do While m.lSuccess
			m.th32ProcessID = This.PROCESSENTRY32_ToCursor(m.pe32)
			This.GetProcessModule(m.th32ProcessID)
			lSuccess = ( Process32Next(hProcessSnap, @pe32) # 0)
		Enddo
		CloseHandle (m.hProcessSnap)
	Endproc

	Procedure MODULEENTRY32_ToCursor
		Lparameters tcBuffer
		Local ProcessID, szModule, szExeFile
		With This
			m.ProcessID = .Substr2Num(m.tcBuffer,9,4)
			m.tcBuffer  = Substr(m.tcBuffer,33)
			m.szModule  = Left(m.tcBuffer,At(Chr(0),m.tcBuffer)-1)
			m.tcBuffer  = Substr(m.tcBuffer,256+1)
			m.szExeFile = Left(m.tcBuffer,At(Chr(0),m.tcBuffer)-1)

			Insert Into (.ModuleListCursor) ;
				(ProcessID,szModule,szExeFile) Values ;
				(m.ProcessID,m.szModule,m.szExeFile)
		Endwith
	Endproc

	Procedure PROCESSENTRY32_ToCursor
		Lparameters tcBuffer
		Local ProcessID, cntThreads, parentPID, szExeFile
		With This
			m.ProcessID  = .Substr2Num(m.tcBuffer,9,4)
			m.cntThreads = .Substr2Num(m.tcBuffer,21,4)
			m.parentPID  = .Substr2Num(m.tcBuffer,25,4)
			m.tcBuffer   = Substr(m.tcBuffer,37)
			m.szExeFile  = Left(m.tcBuffer,At(Chr(0),m.tcBuffer)-1)

			Insert Into (.ProcListCursor) ;
				(ProcessID,	cntThreads, parentPID, szExeFile) ;
				values ;
				(m.ProcessID, m.cntThreads, m.parentPID, m.szExeFile)
		Endwith
		Return m.ProcessID
	Endproc

	Procedure Substr2Num
		Lparameters tcStr, tnStart, tnSize
		Return This.Str2Num(Substr(m.tcStr, m.tnStart, m.tnSize), m.tnSize)
	Endproc

	Procedure Str2Num
		Lparameters tcStr,tnSize
		Local m.lnValue
		m.lnValue=0
		RtlMoveMemory(@lnValue, m.tcStr, m.tnSize)
		Return m.lnValue
	Endproc

	Procedure Int2Str
		Lparameters tnValue, tnSize
		Local ix, lcReturn
		m.tnSize = Iif(Empty(m.tnSize),4,m.tnSize)
		lcReturn = ''
		For ix=1 To m.tnSize
			m.lcReturn = m.lcReturn + ;
				Chr(Bitand(Bitrshift(m.tnValue, (m.ix-1)*8),0xFF))
		Endfor
		Return m.lcReturn
	Endproc

	Procedure CloseApplication(tnHwnd)
		PostMessage(m.tnHwnd,WM_CLOSE,0,0)
	Endproc
Enddefine
Çetin Basöz

The way to Go
Flutter - For mobile, web and desktop.
World's most advanced open source relational database.
.Net for foxheads - Blog (main)
FoxSharp - Blog (mirror)
Welcome to FoxyClasses

LinqPad - C#,VB,F#,SQL,eSQL ... scratchpad
Previous
Reply
Map
View

Click here to load this message in the networking platform