Create Process

BlitzMax Forums/BlitzMax Programming/Create Process

How can i do for launch a process from BMax.
I'm looking for commands like execFile or CreateProcess ?

You can use this command : system_(command:String) eg: system_("notepad.exe")

Note: Your App will wait until the Process you've created is closed.

Thank's a lot, but where can i find information on this command?

I have submitted a code archive entry that will allow your application to keep running in the background.

The down side is that its windows only :( (I'm trying to find a more cross platform way at the moment and have just got my hands on a mac and will also see if i can get the linux version working under FreeBSD soonish but in throey the max/linux process code should be the same.)

Really nice work! But, is it possible with your class to execute a command in 1 line and not 2. The software witch i use in remote can be launch(GUI) or execute(script)

Any idea how to terminate the program? I would like to do it by sending a WM_CLOSE message to the program, so that is terminates "naturally" and safely. This doesn't seem to do it.

FindWindow() can be used to return the window and it works when I send the close message, but that is a really cheesy way of handling it...if you have more than one instance of the program running, it screws up.
Extern "Win32"
	Function WaitForSingleObject:Int(hHandle:Int, dwMilliseconds:Int)
	Function GetExitCodeProcess:Int(hProcess:Int, lpExitCode:Int)
	Function CreateProcessA:Int(lpApplicationName:Byte Ptr, lpCommandLine:Byte Ptr, lpProcessAttributes:Int, lpThreadAttributes:Int, bInheritHandles:Int, dwCreationFlags:Int, lpEnvironment:Int, lpCurrentDirectory:Byte Ptr, lpStartupInfo:ProcessStartUpInfo, lpProcessInformation:ProcessInformation)
	Function CloseHandle:Int(hProcess:Int)
	Function GetActiveWindow:Int()
	Function SetForegroundWindow(hWnd:Int)
	Function SetActiveWindow(hWnd:Int)
	Function OpenProcess:Int(dwDesiredAccess:Int, bInheritHandle:Int, dwProcessId:Int)
	Function keybd_event(bVk:Byte, bScan:Byte, dwFlags:Int, dwExtraInfo:Int)
End Extern





Local p:Process = New Process
p.setExe("c:\windows\notepad.exe")
p.setArgs("C:\Program Files\BlitzMax\doc\home.html")
p.create()

Repeat
	If p.isRunning()=0 Exit
	SendMessageA(p.myhwnd,WM_CLOSE,0,0)
	Print "Process is running..."
	Delay(1000)
Forever

Print "Process finished with return value " + p.getReturnValue()




Type ProcessStartUpInfo
   Field cb:Int
   Field lpReserved:String
   Field lpDesktop:String
   Field lpTitle:String
   Field dwX:Int
   Field dwY:Int
   Field dwXSize:Int
   Field dwYSize:Int
   Field dwXCountChars:Int
   Field dwYCountChars:Int
   Field dwFillAttribute:Int
   Field dwFlags:Int
   Field wShowWindow:Int
   Field cbReserved2:Int
   Field lpReserved2:Int
   Field hStdInput:Int
   Field hStdOutput:Int
   Field hStdError:Int
End Type

Type ProcessInformation
   Field hProcess:Int
   Field hThread:Int
   Field dwProcessID:Int
   Field dwThreadID:Int
End Type

Type Process

	Field exe:String
	Field args:String
	Field proc:ProcessInformation
	Field returnValue:Int
	Field pHandle:Int
	Field myhWnd:Int
	
	Method New()
		myHwnd = GetActiveWindow()
	End Method
	
	Method setExe (appPath:String)
		exe = appPath
	End Method

	Method setArgs (argString:String)
		args = argString
	End Method
	
	Method getReturnValue:Int ()
		Return returnValue
	End Method
	
	Method create()
		' change to exe directory
		ChangeDir(ExtractDir(exe))
		' spawn process...
		Local cmd:String  = "~q" + exe + "~q" + " " + "~q" + args + "~q"
		Print "Run process: " + cmd
		Local start:ProcessStartUpInfo = New ProcessStartUpInfo
		proc = New ProcessInformation
		CreateProcessA(Null, cmd.ToCString(), 0, 0, 1, 0, 0, Null, start, proc)
		pHandle     = OpenProcess($100000, -1, proc.hProcess)
		returnValue = Null
	End Method
	
	Method isRunning:Int()
		Local v:Int = WaitForSingleObject(pHandle, 0)
		Select v
			Case 0
				returnValue = GetExitCodeProcess(pHandle, v)
				CloseHandle(pHandle)
				Print "Process finished with return value: " + returnValue
				' change back to this app dir.
				ChangeDir(LaunchDir$)
				' Set focus back to this application.
				SetForegroundWindow(myHWnd)
				SetActiveWindow(myHWnd)
				
				' Since were a full screen app Windows does not like
				' makign you go back into full screen mode so we have
				' to emulate the user pressing Alt + Enter :)
				
				' Add a little delay
				Delay(100)
				' send alt + enter to get back full screen window :)
				keybd_event(KEY_ALT, 0, 0, 0)
				keybd_event(KEY_RETURN, 0, 0, 0)
				' we have to let go of the keys :)
				keybd_event(KEY_RETURN, 0, 2, 0)
				keybd_event(KEY_ALT, 0, 2, 0)
				' app should now be back in focus and on the screen.
				Return 0
			Case -1
				' We normally get this if the process failed to launch.
				Print "WaitForSingleObject failed."
				CloseHandle(pHandle)
				Return 0
			Default
				' Process is running.
				Return v				
		End Select
	End Method
	
End Type


If you created the process then you have the handle and the processID. You should then use an EnumWindows function to find the window and it's HWND based on that information.
EnumWindows((WNDENUMPROC)EmbedAppEnum, (LPARAM)AppProcessID);


Okay, this adds a Kill() method to the process type. Run it in debug and watch the log. There's a "bad refs" error when the process ends.
Extern "Win32"
	Function WaitForSingleObject:Int(hHandle:Int, dwMilliseconds:Int)
	Function GetExitCodeProcess:Int(hProcess:Int, lpExitCode:Int)
	Function CreateProcessA:Int(lpApplicationName:Byte Ptr, lpCommandLine:Byte Ptr, lpProcessAttributes:Int, lpThreadAttributes:Int, bInheritHandles:Int, dwCreationFlags:Int, lpEnvironment:Int, lpCurrentDirectory:Byte Ptr, lpStartupInfo:ProcessStartUpInfo, lpProcessInformation:ProcessInformation)
	Function CloseHandle:Int(hProcess:Int)
	Function GetActiveWindow:Int()
	Function SetForegroundWindow(hWnd:Int)
	Function SetActiveWindow(hWnd:Int)
	Function OpenProcess:Int(dwDesiredAccess:Int, bInheritHandle:Int, dwProcessId:Int)
	Function keybd_event(bVk:Byte, bScan:Byte, dwFlags:Int, dwExtraInfo:Int)
	Function FindWindowA:Int(ff$z,f$z)
	Function ExitProcess(thing:Int)
	Function EnumWindows:Int(thing:Byte Ptr,f:Int)
	Function GetWindowThreadProcessId:Int(hwnd:Int,g:Byte Ptr)
End Extern

Local p:Process = New Process
p.setExe("c:\windows\notepad.exe")
p.setArgs("C:\Program Files\BlitzMax\doc\home.html")
p.create()

DebugLog "starting"

Repeat
	Local window:TGadget
	Local button:TGadget
	window=CreateWindow("MaxGui Buttons",40,40,240,300,Null,WINDOW_TITLEBAR)
	button=CreateButton("Kill the Process",10,10,140,24,window,BUTTON_PUSH)
	While True
		WaitEvent 
		Select EventID()
			Case EVENT_WINDOWCLOSE
				End
			Case EVENT_GADGETACTION
				If EventSource()=button
					If p
						If p.kill()
							p=Null
							Notify "Killed the process."
						Else
							Notify "Failed to kill the process."
						EndIf
					EndIf
				EndIf
		End Select
	Wend
Forever


Type ProcessStartUpInfo
   Field cb:Int
   Field lpReserved:String
   Field lpDesktop:String
   Field lpTitle:String
   Field dwX:Int
   Field dwY:Int
   Field dwXSize:Int
   Field dwYSize:Int
   Field dwXCountChars:Int
   Field dwYCountChars:Int
   Field dwFillAttribute:Int
   Field dwFlags:Int
   Field wShowWindow:Int
   Field cbReserved2:Int
   Field lpReserved2:Int
   Field hStdInput:Int
   Field hStdOutput:Int
   Field hStdError:Int
End Type

Type ProcessInformation
   Field hProcess:Int
   Field hThread:Int
   Field dwProcessID:Int
   Field dwThreadID:Int
End Type

Type Process

	Field exe:String
	Field args:String
	Field proc:ProcessInformation
	Field returnValue:Int
	Field pHandle:Int
	Field myhWnd:Int
	Field hprocess

	Function EnumWindowsProc:Int(hwnd:Int,lparam:Int)
		Local proc:Int
		GetWindowThreadProcessId(hwnd,Varptr proc)
		If proc=lparam SendMessageA hwnd,WM_CLOSE,0,0
		Return True
	EndFunction
	
	Method Kill:Int(timeout=1000)
		If isRunning()=0 Return True
		EnumWindows(EnumWindowsProc,hProcess)
		Delay timeout
		If isRunning()=0 Return True Else Return False
	EndMethod
	
	Method New()
		myHwnd = GetActiveWindow()
	End Method
	
	Method setExe (appPath:String)
		exe = appPath
	End Method

	Method setArgs (argString:String)
		args = argString
	End Method
	
	Method getReturnValue:Int ()
		Return returnValue
	End Method
	
	Method create()
		' change to exe directory
		ChangeDir(ExtractDir(exe))
		' spawn process...
		Local cmd:String  = "~q" + exe + "~q" + " " + "~q" + args + "~q"
		'Print "Run process: " + cmd
		Local start:ProcessStartUpInfo = New ProcessStartUpInfo
		proc = New ProcessInformation
		CreateProcessA(Null, cmd.ToCString(), 0, 0, 1, 0, 0, Null, start, proc)
		pHandle     = OpenProcess($100000, -1, proc.hProcess)
		returnValue = Null
		hprocess=proc.hprocess
	End Method
	
	Method isRunning:Int()
		Local v:Int = WaitForSingleObject(pHandle, 0)
		Select v
			Case 0
				returnValue = GetExitCodeProcess(pHandle, v)
				CloseHandle(pHandle)
				'Print "Process finished with return value: " + returnValue
				' change back to this app dir.
				ChangeDir(LaunchDir$)
				' Set focus back to this application.
				SetForegroundWindow(myHWnd)
				SetActiveWindow(myHWnd)
				
				' Since were a full screen app Windows does not like
				' makign you go back into full screen mode so we have
				' to emulate the user pressing Alt + Enter :)
				
				' Add a little delay
				Delay(100)
				' send alt + enter to get back full screen window :)
				keybd_event(KEY_ALT, 0, 0, 0)
				keybd_event(KEY_RETURN, 0, 0, 0)
				' we have to let go of the keys :)
				keybd_event(KEY_RETURN, 0, 2, 0)
				keybd_event(KEY_ALT, 0, 2, 0)
				' app should now be back in focus and on the screen.
				Return 0
			Case -1
				' We normally get this if the process failed to launch.
				'Print "WaitForSingleObject failed."
				CloseHandle(pHandle)
				Return 0
			Default
				' Process is running.
				Return v				
		End Select
	End Method
	
End Type


Why don't you just use Skid's PUB.FreeProcess module (available via syncmods)? It's cross-platform, easy to use, and very adaptable...

Here's an example I've made to demonstrate how to use it...

Rem

Windows Notepad PUB.FreeProcess Example

EndRem

Framework MaxGUI.Drivers
Import BRL.EventQueue
Import BRL.Timer
Import PUB.FreeProcess

SuperStrict

Global wndMain:TGadget = CreateWindow("Test Window",100,100,400,300,Null,WINDOW_TITLEBAR|WINDOW_CLIENTCOORDS|WINDOW_STATUS)
	Global gadButton:TGadget = CreateButton("Start",10,10,100,25,wndMain,BUTTON_PUSH)

Global pcsNotepad:TProcess

CreateTimer(2)																	'Create a timer that allows us to check status of process every 500ms

Repeat

	Select WaitEvent()
	
		Case EVENT_WINDOWCLOSE;TProcess.TerminateAll();End							'If the program is closed, terminate all running processes.
		
		Case EVENT_GADGETACTION
		
			Select EventSource()
			
				Case gadButton;If GadgetText$(gadButton) = "Start" Then StartNotepad() Else CloseNotepad()
				
			EndSelect
			
		Case EVENT_TIMERTICK
		
			If pcsNotepad <> Null Then SetStatusText(wndMain,"Status of Notepad Process: " + pcsNotepad.Status()) Else SetStatusText(wndMain,"Object doesn't yet exist.")
			
			If pcsNotepad <> Null And pcsNotepad.Status() = 0 Then SetGadgetText(gadButton,"Start")	'If notepad was closed by user outside the program, update button text.
			
	EndSelect
	
Forever


Function StartNotepad()

	If pcsNotepad <> Null Then pcsNotepad.Terminate()							'Terminate process if it a) exists b) is running.
	
	pcsNotepad = CreateProcess("notepad.exe")									'Open the process notepad.exe and store handle in pcsNotepad
	
	SetGadgetText(gadButton,"Stop")												'Change button text.

EndFunction


Function CloseNotepad()

	If pcsNotepad <> Null And pcsNotepad.Status() Then pcsNotepad.Terminate()	'Terminate process if it a) exists b) is running.
	
	SetGadgetText(gadButton,"Start")											'Change button text.

EndFunction


If there is any example to use the TStream extension? How can a process, in special a other bmax program, receive and send data to the master who create the process?

cu olli

Sorry if I'm just out of the loop, but can you let me know where this syncmods / Pub.FreeProcess stuff can be found? I'm looking for a cross-platform way to launch an external process and not have it block the parent process. I'm sure this has been covered somewhere, but I'm a bit confused about what syncmods is and where I can find a solution for it.

Thanks

Isn't it in Pub?

mod/pub.mod/freeprocess.mod?

Indeed it is. Just found this now. I guess I just missed it since it doesn't appear in the Documentation part of the IDE. Are there any good docs on how to use freeprocess? Either way, it looks fairly easy so I can figure it out, but any good headstarts / documentation / wiki entries would be appreciated.

It does appear to skimp on documentation somewhat (thanks Skid!).

Seb's little example above might help you get some ideas.