

This site is under construction!
| |
A COM component that enables VFP applications to launch
background tasks in separate threads. The main application can retrieve the
result of the executing tasks.
What's new
8/29/05
Fixed error notification to correctly return error info set by the called methods. If called methods uses COMRETURNERROR, the caller will correctly see the error set by the callee.
3/26/03
The DLLs are now built with VC++ 7, you must have the MSVCP70.DLL and MSVCR70.DLL on your machine
Some bugs fixed:
- Running property is now set while worker is running
- 'OnError' method properly called
- Failing to create the worker thread is now reported
- Memory leak in passing the parameters fixed
- Fixed bug that allow to call Run repeatedly, before the worker thread finished
Thanks for everybody making sugestions and reporting bugs!
How it Works
The component is a COM object. You must create an instance using CREATEOBJECT()
function, passing the VFPMTAPP.WORKER.1 class name. This object has two
methods, Run and SinkNotify.
- Run(sSomeCOMClass,sSomeMethod,[arrParams]) This method launches a
background thread, it creates an instance of an objects of class
sSomeCOMClass and it invokes the sSomeMethos method. Optionally, an array of
parameters can be passed. It returns .T. if the working thread has started.
- SinkNotify(oSomeObject,sSomeMethod) This method can be used to
provide the worker object with a VFP object that will receive a notification
when the worker thread has finished. The notification is sent by invoking
the sSomeMethod of oSomeObject and passing the return value returned by the
executed task as a parameter. You must call this method before calling Run
method.
- Running property (read-only) is set to .T. or .F., depending if a
thread is currently executing.
VFPCOM.DLL can be used to bind to the worker thread events
instead of SinkNotify method.
Back to start
Why to use it
The worker object can be used whenever your application has a lengthy task
that can separated into a VFP COM object. The worker thread cannot execute
directly VFP code, it can only create an instance of a COM object and then call
a method of it. Note that the COM object it creates is not necesarily a VFP COM
object. Normal use would involve that this instance does not need user
interaction to perform the task.
Back to start
Examples
A VFP COM MT DLL used by a VFP application
Presume you have a lengthy calculation to be performed by a part of the
application. You can separate this calculation into a separate object that could
perform the calculation when a method is invoked, passing several parameters.
We will define this class as follow:
define
class LengthyServer as Custom OLEPUBLIC
procedure PerformTask(vParam1,vParam2)
* This method should perform some lengthy calculation here. We will simply
return a string from the two parameters :)
return
transform(vParam1,"")+","+transform(vParam2,"")
endproc
enddefine
Now compile a multithreaded DLL that contains the LengthyServer class
definition. Let assume the name of the DLL is SomeServer.DLL, thus the COM
name of our calculation class is SomeServer.LenghtyServer Now lets
create a client that will use the multithreaded capabilities. We will create a
form with three textboxes and a button.
define class ClientForm as Form
m_oWorker= .NULL.
add object TextParam1 as Textbox;
with Left=10, Top=10, Height=25, Width=50
add object TextParam2 as Textbox;
with Left=10, Top=40, Height=25, Width=50
add object TextResult as Textbox;
with Left=10, Top=70, Height=25, Width=50
add object ButtonRun as CommandButton;
with Left=10, Top=100, Height=25, Width=50
procedure Init
* Create the worker object.
this.m_oWorker=createobject('VFPMTAPP.Worker.1')
* Sink thisform as a notified object
* The worker thread will call this.OnFinished when the work is
done
this.m_oWorker.SinkNotified(this,"OnFinished")
endproc
procedure ButtonRun.Click
* Prepare the parameters array
local arrParams[2]
arrParams[1]=thisform.TextParam1.Value
arrParams[2]=thisform.TextParam2.Value
* Launch the worker thread
thisform.m_oWorker.Run("SomeServer.LengthyServer","PerformTask",@arrParams)
endproc
procedure OnFinished(vResult)
* Worker Thread has finished, vResult is the return value
this.TextResult.Value=vResult
this.TextResult.Refresh
endproc
enddefine
When running the
ClientForm, type some value in the first text box, some value in the second text
box and then click on the button. You will see how, after a few seconds, the
result of the task will be displayed in the third textbox. In these seconds, the
form will be active and respond to user events.
Back to start
Error Handling
Errors that happen in the working thread can be reported back to the main
application thread. For this, you must pass a third parameter to the
SinkNotified method:
- SinkNotify(oSomeObject,sSomeMethod,sOnError) The third parameter
is the name of a method to be invoked if an error happens in the worker
thread. The error code (Ox8000xxxx style) and, is available, an error
message are passed as parameters to the sOnError method.
Back to start Notes
- The VFP COM class that does the actual calculation work is not necesarily
a VFP COM object. Any COM class can be used. However, it must be an
automation class, it must expose IDispatch.
- Remeber to compile the VFP COM object using the Multi-Threaded option. You
must have VFP6 with SP3 for this.
- If you feel frustrated by the fact that you can only return a single value
from the worker thread, think again. You can return complex XML that can be
parsed. You can also return an object and inspect its properties. If the
worker thread must return a cursor, you can return an ADO.RecordSet object
and convert it to a cursor into the VFP application. If you return a VFP
object or an ADO.RecordSet, DO NOT CACHE a reference to it, the object
will be destroyed after the the last notification is executed, because the
working thread that hosts this object will end.
- The VFP COM object can very well reside on remote machines, the worker
thread won't notice it. It will instantiate the object on the server
machine, call the method and wait for return. This way you can create
distributed applications without having to wait in the main UI thread for
remote objects to execute and return.
- Do not pass object references to the worker thread object as parameters.
Altough is possible, it will dramatically hurt performance. The reference to
the object will be marshaled into the working thread apartament and when
inspecting the parameters objects, the worker thread must suspend itself,
switch context to the main application thread, inspect the property or
invoke a method, switch context back to the worker thread and resume. This
will slow down both the worker thread and the main application thread.
- When the working thread has finished, the main application thread should
be found in 'read events' state in order to signal the sunk notified
object(s). If the main application thread is not in such a state (e.g.
is in the middle of some lengthy calculation), the worker thread will
suspend until the main thread is available, in order to signal its
termination.
- There are two DLLs because one is a Proxy/Stub DLL needed to marshal the
parameters and the return value between the main application thread and the
worker thread.
- DO NOT RELEASE the worker thread object nor the sunk notified object
before the worker thread has signaled its termination! In our example, it
would be safe to add the following code:
procedure ButtonRun.Click
...
* Launch the worker thread
thisform.m_oWorker.Run("SomeServer.LengthyServer","PerformTask",@arrParams)
this.Enabled=.F
thisform.Closable=.F.
endproc
procedure OnFinished(vResult)
* Worker Thread has finished, vResult is the return value
this.Closable=.T.
this.ButtonRun.Enabled=.T.
this.TextResult.Value=vResult
this.TextResult.Refresh
endproc
Back to start Download
Download/Vfpmtapp.zip Simply download
the zip file, extract the two DLLs in a folder and register them. Back to
start |