Creates a COM object on a seperate thread.
CreateThreadObject(cClassName [, oCallback [, bSyncronousAccess [, nContext [, nStackSize]]]])
Parameters
- cClassName
Specifies the COM class from which the new object is created.
- oCallback
default = .NULL.
An object reference which will receive callbacks when the called methods on the created object complete or an error occurs.
If you pass NULL you won't receive any callbacks on method completion or errors.
If you pass an object it has to implement at least one of these methods:
FUNCTION OnCallComplete(callid, result, callcontext) && callid = a unique call id (integer) that is assigned to each call && result = the return value of the method && callcontext = the callcontext value that was associated with the call
FUNCTION OnError(callid, callcontext, errornumber, errorsource, errordescription) && callid = a unique call id (integer) that is assigned to each call && callcontext = the callcontext value that was associated with the call && errornumber = the errornumber && errorsource = the source of the error that occured && errordescription = the description of the error that occured
Note
You should implement proper COM error handling by using the COMRETURNERROR function inside your COM classes to
make the OnError callback return correct information.
- bSyncronousAccess
default = .F.
If .T. the created proxy object will support the "Object" property to access the created object syncronously.
- nContext
default = CLSCTX_INPROC_SERVER
The execution context of the created COM object.
This parameter can be one of the following values. Execution context Description CLSCTX_INPROC_SERVER The code that creates and manages objects of this class is a DLL that runs in the same process as the caller of the function specifying the class context. CLSCTX_LOCAL_SERVER The EXE code that creates and manages objects of this class runs on same machine but is loaded in a separate process space.
- nStackSize
default = 64 KB
Specifies the stacksize of the created thread in bytes.
Return Value
A proxy object for the created object which allows you to call functions asyncronously.
Remarks
This functions allows you to create real multithreaded FoxPro programs.
Creating multithreaded code in FoxPro was actually possible since you can compile FoxPro code into COM classes. The problem was that you could only run this code in a multithreaded fashion inside other multithreading aware environments / languages like IIS, C++ or C#.
The CreateThreadObject function provides the missing piece in the puzzle - creating multithreading capable COM objects on their own thread from within Visual FoxPro.
Each method call or property access/assignment you make is executed on the thread the created object "lives" in, the call returns immediatly while the method is executed in the background. The return value is always a unique indentifier value (callid) assigned to the call.
All calls are put into a FIFO (first in first out) queue and are processed in the order they are made. The size of the queue is only limited by available memory, so you can queue up as many calls on the object as you like.
The lifetime of the thread is bound to the returned proxy object. If the proxy object is destroyed the thread is destroyed as well.
When the thread is still executing a method when the object is destroyed the destruction will block until the method has finished execution. All other calls that are still in the queue are discarded.
The returned proxy object also implements a few functions for managing method abortion and other tasks.
Type | Name | Description |
---|---|---|
Method | AbortCall(callid) | Aborts the call with the given callid or all calls if 0 is passed. |
Method | GetCallQueueSize | Returns the number of pending call's in the queue. The currently processed call is also counted. |
Method | GetCallRunTime(callid) | Returns the time in milliseconds the call is running. 0 is returned if the call has finished or is still in the queue. If you pass 0 the runtime of the currently processed call is returned. |
Property | Object | When accessing the Object property all calls are made syncronously. This property is only available when you passed .T. in the bSyncronousAccess parameter when creating the object. |
Property | ThreadId | Read only property which returns the thread id on which the object is running. |
Property/Method | CallContext | The CallContext property/method allows you to associate a value (e.g. primary key of a table row) with the next following asyncronous method call. This value is passed on to the completion callback when the method has finished. |
If you implement a property named CallInfo in the COM class you created with CreateThreadObject, you can access this property during the execution of methods
to check for method abortion and other information. The property will return an object which implements the following interface.
Type | Name | Description |
---|---|---|
Property | AbortEvent | Returns the winapi event object (CreateEvent) which is used internally to abort the call. |
Method | Aborted() | Returns .T. if the call was aborted, .F. otherwise. |
Property | CallContext | Returns the value associated with the method call |
Property | CallId | Returns the uniqued id assigned to this call. |
Example
TestFunc blocks for the passed in timeout
TestFunc2 loops, simulates some work with the Sleep api call and checks for method abortion
&& Compile this class into a "Multi-threaded COM server (dll)" DEFINE CLASS ExampleObject AS Session OLEPUBLIC DataSession = 2 && you should always run each object in their own datasession CallInfo = .NULL. && "magic" property FUNCTION TestFunc(lnTimeOut AS Long) AS Long DECLARE Sleep IN WIN32API INTEGER Sleep(m.lnTimeOut * 1000) RETURN m.lnTimeOut ENDFUNC FUNCTION TestFunc2() AS Long LOCAL lnRetVal, xj DECLARE Sleep IN WIN32API INTEGER m.lnRetVal = 1 FOR m.xj = 1 TO 30 Sleep(500) IF THIS.CallInfo.Aborted() m.lnRetVal = -1 EXIT ENDIF ENDFOR RETURN m.lnRetVal ENDFUNC ENDDEFINE
&& using the above class PUBLIC loObj, loCallback, xj, lnCallId m.loCallback = CREATEOBJECT('ExampleCallback') m.loObj = CreateThreadObject('YourProject.ExampleObject', m.loCallback, .T.) FOR m.xj = 1 to 5 ? m.loObj.TestFunc(m.xj) ENDFOR && aborting a call m.lnCallId = m.loObj.TestFunc2() ? m.loObj.AbortCall(m.lnCallId) && associating a value with the next call m.loObj.CallContext = 'someValue' ? m.loObj.TestFunc(5) && you can also use CallContext as a method with chaining (it returns "THIS") ? m.loObj.CallContext('someValue2').TestFunc(5) && making a syncronous call over the "Object" property && this will block until the method has finished just like any ordinary method call ? m.loObj.Object.TestFunc(5) DEFINE CLASS ExampleCallback AS Custom FUNCTION OnCallComplete(callid AS Long, result AS Variant, callcontext AS Variant) AS VOID ? callid, result, callcontext ENDFUNC FUNCTION OnError(callid AS Long, callcontext AS Variant, errornumber AS Long, errorsource AS String, errordescription AS String) AS VOID ? callid, callcontext, errornumber, errorsource, errordescription ENDFUNC ENDDEFINE
See Also
Reference
CLSIDFromProgID
CLSIDFromString
CreateGuid
GetIUnknown
IsEqualGuid
ProgIDFromCLSID
RegisterActiveObject
RegisterObjectAsFileMoniker
RevokeActiveObject
StringFromCLSID
Used WinApi functions
_beginthreadex
CloseHandle
CLSIDFromProgID
CoCreateInstance
CoGetInterfaceAndReleaseStream
CoMarshalInterThreadInterfaceInStream
CoInitializeEx
CoUninitialize
CreateEvent
DispatchMessage
EnterCriticalSection
GetLastError
InitializeCriticalSectionAndSpinCount
LeaveCriticalSection
MsgWaitForMultipleObjects
PeekMessage
ResetEvent
SetEvent
VariantChangeType
VariantClear
VariantInit
VariantCopy
WaitForSingleObject