Chris Miller
Programmer
I programmed something for which I saw a demand from a few past threads about parallel processing, which are:
thread184-1817657
thread184-1819600
thread184-1819365
thread184-1818385
So this is for anybody interested in parallel code execution.
And I know I could point out several things already existing, I even did in posts in these threads. I mentioned the existence of ParallelFox: I mentioned the simplest parallel execution possible with RUN /N another.EXE, possibly with parameters.
I mentioned you could run two executables and do IPC - interprocess communication - with windows messages.
I mentioned an approach of Calvin Hsia to allow multithreading and pointed out it has the problem of being data execution, which Windows prevents with data execution prevention - in short DEP.
And there's more about parallel processing or multithreading with FoxPro out in the internet already, so actually no need to come up with anything new, but I wanted to point out one thing that I think all existing solutions overlooked so far. Making use of an out-of-process COM server as a background process. I'll go into the technical details in one or a few separate posts, this is just the instructions and usage post:
So, without going into details of why to do things this way, just the instructions of what to create and how to use it.
1. Build a COM Server "background.process"
1a) Create a new folder "background"
1b) Open VFP this way:
1c) Create a project in the background folder you call "background.pjx" - that way a build of the project will create a background.EXE
1d) Create this as the main.prg of the background.pjx project
1e) build the EXE
Now you will have a background.process COM server in your system.
2. Using the Background.Process COM server.
This new COM server has the following properties and methods of interest:
Script - a property you set to a script of VFP code you want to execute in parallel.
Executing - this property tells you whether the background process is executing .script
ErrorHappened - tells you whether an error happened
LastReturnValue - property containing the last value returned from the last successfully ran .script
LastError - the last error from the last unsuccessfully ran .script
Execute() - the method to execute the code in the Script property.
Quit() - quitting the background process
I explain why I chose this structure in a separate thread. A simple example of how to use this is:
It's actually a usage that waits for the result, which you'd normally not do, as you want this to run in parallel to what your application does, but printing the Seconds(), then waiting for goBAcgroundPRocess not executing anymore and printing seconds() with the lastreturnvalue proves, that the call goBackgroundprocess.Execute() returns immediately and the return value of the script (return 42) comes back a second later. So this is true multiprocessing.
In general, the usage is to
I. Set goBackgroundprocess.Script to the code you want to execute in parallel
II. call goBackgroundprocess.Execute()
If the script causes an error within goBackgroundprocess, goBackgroundprocess.ErrorHappened will be set .T. and the error message will be put into goBackgroundprocess.LastError. In this version of it, the line of error will always be the ExecScript line of the ExecuteScript method and not the line number within the script that actually caused the error, that's harder to refine, but maybe in an advanced version I'll provide this.
this concludes the "How To" part of it, just a few last remarks; The scripts you can execute will not see any of the main application process variables, not even public variables. It will not see any workareas of any data session, so the scripts you run have to be able to run completely standalone. There is no mechanism foreseen to pass in parameters, but you can of course generate the script code to set some variables to values as you like or need, or you pass data via DBFs the cript opens and scans or by adding properties to the goBackgroundprocess object.
I intentionally use a public variable goBackgroundprocess for the COM server, as you usally want to have the ability to run parallel code anywhere in your application and to avoid the overhead of creating the object, which can take up to a few seconds in the worst case I measured. This way this initialization becomes only a onetime penalty. What's important for the "How To" usage is, that you quit the background process by calling Quit() and releasing the variable goBackgroundprocess, the background.exe will not disappear from the task manager detail tab, if you only call Quit(). It will only disappear once you do the latter, once you release the variable as the last reference keeping the EXE from quitting.
I will describe technical details about this in a late thread and also some advanced ways of using it, which allow usage of code or classes from your own EXE within the background.process.
Chriss
thread184-1817657
thread184-1819600
thread184-1819365
thread184-1818385
So this is for anybody interested in parallel code execution.
And I know I could point out several things already existing, I even did in posts in these threads. I mentioned the existence of ParallelFox: I mentioned the simplest parallel execution possible with RUN /N another.EXE, possibly with parameters.
I mentioned you could run two executables and do IPC - interprocess communication - with windows messages.
I mentioned an approach of Calvin Hsia to allow multithreading and pointed out it has the problem of being data execution, which Windows prevents with data execution prevention - in short DEP.
And there's more about parallel processing or multithreading with FoxPro out in the internet already, so actually no need to come up with anything new, but I wanted to point out one thing that I think all existing solutions overlooked so far. Making use of an out-of-process COM server as a background process. I'll go into the technical details in one or a few separate posts, this is just the instructions and usage post:
So, without going into details of why to do things this way, just the instructions of what to create and how to use it.
1. Build a COM Server "background.process"
1a) Create a new folder "background"
1b) Open VFP this way:
1c) Create a project in the background folder you call "background.pjx" - that way a build of the project will create a background.EXE
1d) Create this as the main.prg of the background.pjx project
Code:
#Define CRLF Chr(13)+Chr(10)
Define Class Process As Form OlePublic
Hidden ExecuteScriptMessage
Script = ''
ExecuteScriptMessage = 0
Executing = .F.
ErrorHappened = .F.
LastReturnValue = .null.
LastError = ''
Procedure Init()
On Shutdown Quit
* modal states are unwanted, which we can enfore:
Sys(2335,0) && enable unattended server mode
Declare Integer RegisterWindowMessage In User32.Dll String cMessage
Declare Integer PostMessage In User32.Dll Integer nHWnd, Integer nMsg, Integer wParams, Integer lParams
This.ExecuteScriptMessage = RegisterWindowMessage('BackgroundProcess_ExecuteQueuedTasks')
If Between(This.ExecuteScriptMessage, 0xC000, 0xFFFF)
Bindevent(Thisform.HWnd, This.ExecuteScriptMessage , This, 'ExecuteScript', 2+4)
Else
Quit
EndIf
Endproc
Procedure Execute() ;
HelpString "Execute code in Script property"
Local llDone
If Not This.Executing && do nothing, if a script still runs
This.Executing = .T.
This.ErrorHappened = .F.
* Postmessage causes ExecuteScript to run, because of the BindEvent in the Init().
PostMessage(Thisform.HWnd, This.ExecuteScriptMessage, 0, 0)
llDone = .T.
EndIf
Return llDone
Endproc
Hidden Procedure ExecuteScript(HWnd As Integer, Msg As Integer, wParam As Integer, Lparam As Integer)
This.LastReturnValue = Execscript(This.Script)
This.Executing = .F.
Return 0
Endproc
Procedure Error(nError, cMethod, nLine)
This.Executing = .F.
This.ErrorHappened = .T.
This.LastError = Textmerge("Error <<nError>> in <<cMethod>> Line <<nLine>>: <<Message()>>",.T.)
Endproc
Procedure Quit() ;
HelpString "end the background process"
Do While Sys(3098,This)>0
DoEvents
Enddo
Endproc
Procedure Destroy()
Sys(2335,1) && disable unattended server mode again
On Shutdown
Quit
Endproc
Enddefine
1e) build the EXE
Now you will have a background.process COM server in your system.
2. Using the Background.Process COM server.
This new COM server has the following properties and methods of interest:
Script - a property you set to a script of VFP code you want to execute in parallel.
Executing - this property tells you whether the background process is executing .script
ErrorHappened - tells you whether an error happened
LastReturnValue - property containing the last value returned from the last successfully ran .script
LastError - the last error from the last unsuccessfully ran .script
Execute() - the method to execute the code in the Script property.
Quit() - quitting the background process
I explain why I chose this structure in a separate thread. A simple example of how to use this is:
Code:
Clear
Public goBackgroundprocess
goBackgroundprocess = Createobject("background.process")
Text To goBackgroundprocess.Script NoShow
Declare Integer Sleep in Win32API Integer milliseconds
Sleep(1000)
Return 42
EndText
goBackgroundprocess.Execute()
? Seconds()
Do while goBackgroundprocess.Executing
Doevents
EndDo
? Seconds(), goBackgroundprocess.LastReturnValue
goBackgroundprocess.Quit()
Release goBackgroundprocess
In general, the usage is to
I. Set goBackgroundprocess.Script to the code you want to execute in parallel
II. call goBackgroundprocess.Execute()
If the script causes an error within goBackgroundprocess, goBackgroundprocess.ErrorHappened will be set .T. and the error message will be put into goBackgroundprocess.LastError. In this version of it, the line of error will always be the ExecScript line of the ExecuteScript method and not the line number within the script that actually caused the error, that's harder to refine, but maybe in an advanced version I'll provide this.
this concludes the "How To" part of it, just a few last remarks; The scripts you can execute will not see any of the main application process variables, not even public variables. It will not see any workareas of any data session, so the scripts you run have to be able to run completely standalone. There is no mechanism foreseen to pass in parameters, but you can of course generate the script code to set some variables to values as you like or need, or you pass data via DBFs the cript opens and scans or by adding properties to the goBackgroundprocess object.
I intentionally use a public variable goBackgroundprocess for the COM server, as you usally want to have the ability to run parallel code anywhere in your application and to avoid the overhead of creating the object, which can take up to a few seconds in the worst case I measured. This way this initialization becomes only a onetime penalty. What's important for the "How To" usage is, that you quit the background process by calling Quit() and releasing the variable goBackgroundprocess, the background.exe will not disappear from the task manager detail tab, if you only call Quit(). It will only disappear once you do the latter, once you release the variable as the last reference keeping the EXE from quitting.
I will describe technical details about this in a late thread and also some advanced ways of using it, which allow usage of code or classes from your own EXE within the background.process.
Chriss