Home
Up

Under construction!

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

  1. 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.
  2. Remeber to compile the VFP COM object using the Multi-Threaded option. You must have VFP6 with SP3 for this.
  3. 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.
  4. 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.
  5. 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.
  6. 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. 
  7. 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.
  8. 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