Code: Select all
;=================================================================================
;Returns a COMate object or an iUnknown interface pointer depending on the value of the 'objectType' parameter.
;For 'regular' objects based upon iDispatch, leave the optional parameter 'objectType' as it is.
;Otherwise, for unknown object types set objectType to equal #COMate_UnknownObjectType. In these cases, this method will return the
;interface pointer directly (as opposed to a COMate object).
;In either case the object should be released as soon as it is no longer required.
;Any HRESULT return value is accessible through the GetLastErrorCode() method.
Procedure.i COMateClass_GetObjectProperty(*this._membersCOMateClass, command$, *hStatement=0, objectType = #VT_DISPATCH)
Protected result.i = #S_OK, retVar.VARIANT, *newObject._membersCOMateClass
If command$ Or *hStatement
If objectType <> #VT_DISPATCH
objectType = #VT_UNKNOWN
EndIf
result = COMateClass_INTERNAL_InvokePlus(*this, #DISPATCH_PROPERTYGET|#DISPATCH_METHOD, objectType, retVar, command$, *hStatement)
If SUCCEEDED(result)
If objectType = #VT_DISPATCH
If retVar\pdispVal
;We now create a COMate object to house this instance variable.
*newObject = AllocateMemory(SizeOf(_membersCOMateClass))
If *newObject
*newObject\vTable = ?VTable_COMateClass
*newObject\iDisp = retVar\pdispVal
COMate_gNumObjects+1
Else
VariantClear_(retVar) ;This will call the Release() method of the COM object.
result = #E_OUTOFMEMORY
EndIf
Else
VariantClear_(retVar)
;In this case we set an error with extra info.
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, #S_FALSE, 0, "The property returned a NULL object!")
CompilerEndIf
result = -1
EndIf
Else
*newObject = retVar\punkVal
EndIf
Else
VariantClear_(retVar)
EndIf
Else
result = #E_INVALIDARG
EndIf
;Set any error code. iDispatch errors will alreay have been set.
If result <> -1
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result)
CompilerEndIf
EndIf
ProcedureReturn *newObject
EndProcedure
;=================================================================================
;=================================================================================
;The following method calls a dispinterface function and returns a double value.
;Any HRESULT return value is accessible through the GetLastErrorCode() method.
Procedure.d COMateClass_GetRealProperty(*this._membersCOMateClass, command$, *hStatement=0)
Protected result.i = #S_OK, retVar.VARIANT, retValue.d
If command$ Or *hStatement
result = COMateClass_INTERNAL_InvokePlus(*this, #DISPATCH_PROPERTYGET|#DISPATCH_METHOD, #VT_R8, retVar, command$, *hStatement)
If SUCCEEDED(result)
retValue = retVar\dblval
EndIf
VariantClear_(retVar)
Else
result = #E_INVALIDARG
EndIf
;Set any error code. iDispatch errors will alreay have been set.
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result)
CompilerEndIf
ProcedureReturn retValue
EndProcedure
;=================================================================================
;=================================================================================
;The following method calls a dispinterface function and returns a string value.
;Any HRESULT return value is accessible through the GetLastErrorCode() method.
Procedure.s COMateClass_GetStringProperty(*this._membersCOMateClass, command$, *hStatement=0)
Protected result.i = #S_OK, retVar.VARIANT, result$
If command$ Or *hStatement
result = COMateClass_INTERNAL_InvokePlus(*this, #DISPATCH_PROPERTYGET|#DISPATCH_METHOD, #VT_BSTR, retVar, command$, *hStatement)
If SUCCEEDED(result) And retVar\bstrVal
result$ = PeekS(retVar\bstrVal, -1, #PB_Unicode)
EndIf
VariantClear_(retVar)
Else
result = #E_INVALIDARG
EndIf
;Set any error code. iDispatch errors will alreay have been set.
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result)
CompilerEndIf
ProcedureReturn result$
EndProcedure
;=================================================================================
;=================================================================================
;The following method calls a dispinterface function and, if there are no errors, returns a pointer to a new variant which must be
;'freed' by the user with VariantClear_() etc.
;Any HRESULT return value is accessible through the GetLastErrorCode() method.
Procedure.i COMateClass_GetVariantProperty(*this._membersCOMateClass, command$, *hStatement=0)
Protected result.i = #S_OK, *retVar.VARIANT
If command$ Or *hStatement
;Allocate memory for a new variant.
*retVar = AllocateMemory(SizeOf(VARIANT))
If *retVar
result = COMateClass_INTERNAL_InvokePlus(*this, #DISPATCH_PROPERTYGET|#DISPATCH_METHOD, #VT_EMPTY, *retVar, command$, *hStatement)
If FAILED(result)
FreeMemory(*retVar)
*retVar = 0
EndIf
Else
result = #E_OUTOFMEMORY
EndIf
Else
result = #E_INVALIDARG
EndIf
;Set any error code. iDispatch errors will alreay have been set.
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result)
CompilerEndIf
ProcedureReturn *retVar
EndProcedure
;=================================================================================
;=================================================================================
;The following method calls a dispinterface method where no return value is required.
;Returns a HRESULT value. #S_OK for no errors.
;Errors reported by the methods called by the user will be reported elsewhere (eventually!)
Procedure.i COMateClass_SetProperty(*this._membersCOMateClass, command$, *hStatement=0)
Protected result.i = #S_OK
If command$ Or *hStatement
result = COMateClass_INTERNAL_InvokePlus(*this, #DISPATCH_PROPERTYPUT, #VT_EMPTY, 0, command$, *hStatement)
Else
result = #E_INVALIDARG
EndIf
;Set any error code. iDispatch errors will alreay have been set.
If result = -1
result = #S_FALSE
Else
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result)
CompilerEndIf
EndIf
ProcedureReturn result
EndProcedure
;=================================================================================
;=================================================================================
;The following function calls a dispinterface method where no return value is required.
;Returns a HRESULT value. #S_OK for no errors.
;Errors reported by the methods called by the user will be reported elsewhere (eventually!)
Procedure.i COMateClass_SetPropertyRef(*this._membersCOMateClass, command$, *hStatement=0)
Protected result.i = #S_OK
If command$ Or *hStatement
result = COMateClass_INTERNAL_InvokePlus(*this, #DISPATCH_PROPERTYPUTREF, #VT_EMPTY, 0, command$, *hStatement)
Else
result = #E_INVALIDARG
EndIf
;Set any error code. iDispatch errors will alreay have been set.
If result = -1
result = #S_FALSE
Else
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result)
CompilerEndIf
EndIf
ProcedureReturn result
EndProcedure
;=================================================================================
CompilerIf Defined(COMATE_NOINCLUDEATL, #PB_Constant)=0
;-COMate CLASS - EVENT RELATED METHODS.
;---------------------------------------------------------------------------------
;=================================================================================
;The following method attaches an event handler from the user's program to the underlying COM object. (Code based on that written by Freak.)
;Set callback to zero to remove any existing callback.
;Returns a HRESULT value. #S_OK for no errors.
Procedure.i COMateClass_SetEventHandler(*this._membersCOMateClass, eventName$, callback, returnType = #COMate_NORETURN, *riid.IID=0)
Protected result.i = #S_OK
Protected container.IConnectionPointContainer, enum.IEnumConnectionPoints, connection.IConnectionPoint, connIID.IID
Protected dispTypeInfo.ITypeInfo, typeLib.ITypeLib, typeInfo.ITypeInfo
Protected infoCount, index
Protected *sink._COMateEventSink, newSink.IDispatch
If eventName$ = #COMate_CatchAllEvents Or *this\hWnd
If returnType < #COMate_NoReturn Or returnType > #COMate_OtherReturn
returnType = #COMate_NoReturn
EndIf
If eventName$ = #COMate_CatchAllEvents
If returnType <> #COMate_NORETURN
returnType = #COMate_OtherReturn ;No sense in an explicit return value when dealing with any event!
EndIf
;If their already exists a sink for this object then we just switch the main callback.
If *this\eventSink And callback
*this\eventSink\callback = callback
*this\eventSink\returnType = returnType
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result)
CompilerEndIf
ProcedureReturn result ;No error.
ElseIf *this\eventSink = 0 And callback = 0 ;No point proceeding with this.
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result)
CompilerEndIf
ProcedureReturn result ;No error reported.
EndIf
ElseIf *this\eventSink
If callback And *this\hWnd
SetProp_(*this\hWnd, eventName$+"_COMate", callback)
SetProp_(*this\hWnd, eventName$+"_RETURN_COMate", returnType)
ElseIf *this\hWnd
RemoveProp_(*this\hWnd, eventName$+"_COMate")
RemoveProp_(*this\hWnd, eventName$+"_RETURN_COMate")
EndIf
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result)
CompilerEndIf
ProcedureReturn result
ElseIf callback = 0 ;*this\eventSink will equal 0 as well.
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result)
CompilerEndIf
ProcedureReturn result
EndIf
;Only remaining options are for wishing to remove a previously installed sink (requires #COMate_CatchAllEvents) or a completely new sink is to be installed.
result = *this\iDisp\GetTypeInfoCount(@infoCount)
If SUCCEEDED(result)
If InfoCount = 1
result = *this\iDisp\GetTypeInfo(0, 0, @dispTypeInfo)
If SUCCEEDED(result)
result = dispTypeInfo\GetContainingTypeLib(@typeLib, @index)
If SUCCEEDED(result)
result = *this\iDisp\QueryInterface(?IID_IConnectionPointContainer, @container)
If SUCCEEDED(result)
If *riid.IID = 0
result = container\EnumConnectionPoints(@enum.IEnumConnectionPoints)
If SUCCEEDED(result)
enum\Reset()
result = enum\Next(1, @connection, #Null)
While result = #S_OK
result = Connection\GetConnectionInterface(@connIID)
If SUCCEEDED(result) ;We have a valid IID for the outgoing interface managed by this connection point.
result = typeLib\GetTypeInfoOfGuid(connIID, @typeInfo)
If SUCCEEDED(result)
enum\Release()
Goto COMateClass_SetEventHandler_L1
EndIf
EndIf
connection\Release()
result = enum\Next(1, @connection, #Null)
Wend
enum\Release()
EndIf
Else ;The user has specified a connection point interface.
result = container\FindConnectionPoint(*riid, @connection)
If SUCCEEDED(result)
result = Connection\GetConnectionInterface(@connIID) ;May or may not equal the IID pointed to by *riid.
If SUCCEEDED(result) ;We have a valid IID for the outgoing interface managed by this connection point.
result = typeLib\GetTypeInfoOfGuid(*riid, @typeInfo)
If SUCCEEDED(result)
COMateClass_SetEventHandler_L1:
If eventName$ = #COMate_CatchAllEvents And callback = 0 ;Remove existing sink.
;The Unadvise() method will call Release() on our sink and so we leave all tidying up to this method.
connection\Unadvise(*this\eventSink\cookie)
TypeInfo\Release()
Else ;New sink needs creating.
*sink = AllocateMemory(SizeOf(_COMateEventSink))
If *sink
*this\eventSink = *sink
With *this\eventSink
\Vtbl = ?VTable_COMateEventSink
\refCount = 1
\typeInfo = typeInfo
If eventName$ = #COMate_CatchAllEvents
\callback = Callback
\returnType = returnType
ElseIf *this\hWnd
SetProp_(*this\hWnd, eventName$+"_COMate", callback)
SetProp_(*this\hWnd, eventName$+"_RETURN_COMate", returnType)
EndIf
CopyMemory(connIID, @\connIID, SizeOf(IID))
\parent = *this
EndWith
newSink = *sink
result = connection\Advise(newSink, @*this\eventSink\cookie) ;Calls QueryInterface() on NewSink hence the subsequent Release().
;In the case of an error this release will decrement the ref counter to zero and then tidy up!
NewSink\Release()
Else
TypeInfo\Release()
result = #E_OUTOFMEMORY
EndIf
EndIf
EndIf
EndIf
connection\Release()
EndIf
EndIf
container\Release()
EndIf
typeLib\Release()
EndIf
dispTypeInfo\Release()
EndIf
EndIf
EndIf
Else
result = #E_FAIL
EndIf
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result)
CompilerEndIf
ProcedureReturn result
EndProcedure
;=================================================================================
;=================================================================================
;The following method, valid only when called from a user's event procedure, retrieves the specified parameter in the form
;of a quad.
Procedure.q COMateClass_GetIntegerEventParam(*this._membersCOMateClass, index)
Protected result.i = #S_OK, var.VARIANT, puArgErr
If *this\eventSink And *this\eventSink\dispParams
If index > 0 And index <= *this\eventSink\dispParams\cArgs+*this\eventSink\dispParams\cNamedArgs
result = DispGetParam_(*this\eventSink\dispParams, index-1, #VT_I8, var, @puArgErr)
Else
result = #E_INVALIDARG
EndIf
Else
result = #E_FAIL
EndIf
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result)
CompilerEndIf
ProcedureReturn var\llval
EndProcedure
;=================================================================================
;=================================================================================
;The following method, valid only when called from a user's event procedure, retrieves the specified parameter in the form
;of a COM interface. It does not wrap any returned object into a COMate object. Returns zero if an error.
;The user MUST call Release() on any object returned.
;Leave objectType = #VT_DISPATCH to have an iDispatch interface returned. Any other value will result in an iUnknown interface.
Procedure.i COMateClass_GetObjectEventParam(*this._membersCOMateClass, index, objectType = #VT_DISPATCH)
Protected result.i = #S_OK, var.VARIANT, puArgErr
If *this\eventSink And *this\eventSink\dispParams
If index > 0 And index <= *this\eventSink\dispParams\cArgs+*this\eventSink\dispParams\cNamedArgs
If objectType <> #VT_DISPATCH
objectType = #VT_UNKNOWN
EndIf
result = DispGetParam_(*this\eventSink\dispParams, index-1, objectType, @var, @puArgErr)
Else
result = #E_INVALIDARG
EndIf
Else
result = #E_FAIL
EndIf
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result)
CompilerEndIf
ProcedureReturn var\pDispVal
EndProcedure
;=================================================================================
;=================================================================================
;The following method, valid only when called from a user's event procedure, retrieves the specified parameter in the form
;of a double.
Procedure.d COMateClass_GetRealEventParam(*this._membersCOMateClass, index)
Protected result.i = #S_OK, var.VARIANT, puArgErr
If *this\eventSink And *this\eventSink\dispParams
If index > 0 And index <= *this\eventSink\dispParams\cArgs+*this\eventSink\dispParams\cNamedArgs
result = DispGetParam_(*this\eventSink\dispParams, index-1, #VT_R8, var, @puArgErr)
Else
result = #E_INVALIDARG
EndIf
Else
result = #E_FAIL
EndIf
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result)
CompilerEndIf
ProcedureReturn var\dblVal
EndProcedure
;=================================================================================
;=================================================================================
;The following method, valid only when called from a user's event procedure, retrieves the specified parameter in the form
;of a string.
Procedure.s COMateClass_GetStringEventParam(*this._membersCOMateClass, index)
Protected result.i = #S_OK, var.VARIANT, puArgErr, text$
If *this\eventSink And *this\eventSink\dispParams
If index > 0 And index <= *this\eventSink\dispParams\cArgs+*this\eventSink\dispParams\cNamedArgs
result = DispGetParam_(*this\eventSink\dispParams, index-1, #VT_BSTR, var, @puArgErr)
If var\bstrVal
text$ = PeekS(var\bstrVal, -1, #PB_Unicode)
SysFreeString_(var\bstrVal)
EndIf
Else
result = #E_INVALIDARG
EndIf
Else
result = #E_FAIL
EndIf
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result)
CompilerEndIf
ProcedureReturn text$
EndProcedure
;=================================================================================
;=================================================================================
;The following method, valid only when called from a user's event procedure, returns 0 if the specified parameter was
;not passed by reference.
;Otherwise, it returns the variant #VT_... type of the underlying parameter and it places the address of the underlying
;parameter into the *ptrParameter parameter (if non-zero). This allows the client application to alter the value of the parameter
;as appropriate.
;For even more flexibility, you can obtain a pointer to the actual variant containing the parameter as supplied by the ActiveX control.
Procedure.i COMateClass_IsEventParamPassedByRef(*this._membersCOMateClass, index, *ptrParameter.INTEGER=0, *ptrVariant.INTEGER=0)
Protected result.i = #S_OK, *var.VARIANT, *ptr.INTEGER, numArgs
If *this\eventSink And *this\eventSink\dispParams
numArgs = *this\eventSink\dispParams\cArgs+*this\eventSink\dispParams\cNamedArgs
If index > 0 And index <= numArgs
*var = *this\eventSink\dispParams\rgvarg + (numArgs-index)*SizeOf(VARIANT)
If *var\vt&#VT_BYREF
result = *var\vt&~#VT_BYREF
If *ptrParameter
*ptrParameter\i = *var\pllval
EndIf
If *ptrVariant
*ptrVariant\i = *var
EndIf
EndIf
Else
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, #E_INVALIDARG)
CompilerEndIf
EndIf
Else
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, #E_FAIL)
CompilerEndIf
EndIf
ProcedureReturn result
EndProcedure
;=================================================================================
CompilerEndIf
;/////////////////////////////////////////////////////////////////////////////////
;The following function is called by the COMatePLUS_CompileSubobjectInvokation() function when extracting method arguments and only when the final
;option is a numeric argument.
;Returns #True if a valid variant numeric type is found and also places the relevant value within the given variant.
Procedure.i COMate_INTERNAL_CheckNumeric(arg$, *var.VARIANT)
Protected result.i = #True, i, blnPoint, length, *ptr.CHARACTER
Protected val.q, byte.b, word.w, long.l
length = Len(arg$)
*ptr = @arg$
For i = 1 To length
If *ptr\c = '-' Or *ptr\c = '+'
If i > 1
result = 0
Break
EndIf
ElseIf *ptr\c = '.' And blnPoint = #False
blnPoint = #True
ElseIf *ptr\c < '0' Or *ptr\c > '9'
result = 0
Break
EndIf
*ptr+SizeOf(CHARACTER)
Next
If result
If blnPoint ;Decimal.
*var\vt = #VT_R8
*var\dblVal = ValD(arg$)
Else ;Some kind of integer.
val = Val(arg$)
If val = 0 ;Shove this into a signed 'long'.
*var\vt = #VT_I4
*var\lVal = 0
Else
;Check if the value will fit into a signed-byte or a signed-word or a signed-long or a signed-quad.
byte = val
If byte = val ;Signed byte.
*var\vt = #VT_I1
*var\cVal = val
Else
word = val
If word = val ;Signed word.
*var\vt = #VT_I2
*var\iVal = val
Else
long = val
If long = val ;Signed long.
*var\vt = #VT_I4
*var\lVal = val
Else ;Quad.
*var\vt = #VT_I8
*var\llVal = val
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
ProcedureReturn result
EndProcedure
;/////////////////////////////////////////////////////////////////////////////////
;/////////////////////////////////////////////////////////////////////////////////
;The following function is called by the COMatePLUS_CompileSubobjectInvokation() function when extracting method arguments and only for non-empty strings.
;Quoted strings (beginning with ') can contain escaped sequences of the form $xxxx where xxxx represent a hex number.
;Together $xxxx represents a single character code; e.g. an Ascii code. E.g. $0024 would be replaced by a $ character and
;$0027 would be replaced by a ' character.
;Adjusts the string in-place.
Procedure COMate_INTERNAL_EscapeString(ptrText)
Protected *source.CHARACTER, *destination.CHARACTER, blnEscape, value, t1, pow, i
*source.CHARACTER = ptrText
*destination = *source
While *source\c
If *source\c = 36
;Is this the beginning of an escape sequence?
blnEscape = #True
t1 = *source
value = 0
pow = 4096 ;16^3.
For i = 1 To 4
*source + SizeOf(CHARACTER)
If *source\c = 0 ;Null terminator.
Break 2
ElseIf *source\c >= '0' And *source\c <= '9'
value + (*source\c-'0')*pow
ElseIf *source\c >= 'A' And *source\c <= 'F'
value + (*source\c-'A'+10)*pow
ElseIf *source\c >= 'a' And *source\c <= 'f'
value + (*source\c-'a'+10)*pow
Else
blnEscape = #False
Break
EndIf
pow>>4
Next
If blnEscape ;We have an escape sequence.
*destination\c = value&$ff : *destination + SizeOf(CHARACTER)
*source + SizeOf(CHARACTER)
Else
*source = t1
Goto COMate_labelEscape1
EndIf
Else
COMate_labelEscape1:
*destination\c = *source\c
*destination + SizeOf(CHARACTER)
*source + SizeOf(CHARACTER)
EndIf
Wend
*destination\c = 0 ;Null termminator.
EndProcedure
;/////////////////////////////////////////////////////////////////////////////////
;/////////////////////////////////////////////////////////////////////////////////
;The following function is called (possibly more than once) by the COMateClass_INTERNAL_InvokePlus as we drill down through
;subobject method calls etc. This performs the task of calling the dispinterface methods.
;Returns a HRESULT value; #S_OK for no errors.
Procedure.i COMateClass_INTERNAL_InvokeiDispatch(*this._membersCOMateClass, invokeType, returnType, *ret.VARIANT, iDisp.iDispatch, subObjectIndex, *statement._COMatePLUSStatement)
Protected result.i = #S_OK
Protected dispID, dp.DISPPARAMS, dispIDNamed, excep.EXCEPINFO2, uiArgErr
;First task is to retrieve the dispID corresponding to the method/property.
result = iDisp\GetIDsOfNames(?IID_NULL, @*statement\methodName[subObjectIndex], 1, #LOCALE_USER_DEFAULT, @dispID)
If SUCCEEDED(result)
;Now prepare to call the method/property.
dispidNamed = #DISPID_PROPERTYPUT
If *statement\numArgs[subObjectIndex]
dp\rgvarg = *statement\ptrVarArgs[subObjectIndex] + (#COMate_MAXNUMVARIANTARGS - *statement\numArgs[subObjectIndex])*SizeOf(VARIANT)
EndIf
dp\cargs = *statement\numArgs[subObjectIndex]
If invokeType & (#DISPATCH_PROPERTYPUT | #DISPATCH_PROPERTYPUTREF)
dp\cNamedArgs = 1
dp\rgdispidNamedArgs = @dispidNamed
EndIf
;Call the method/property.
result = iDisp\Invoke(dispID, ?IID_NULL, #LOCALE_USER_DEFAULT, invokeType, dp, *ret, excep, @uiArgErr)
If result = #DISP_E_EXCEPTION
;Has the automation server deferred from filling in the EXCEPINFO2 structure?
If excep\pfnDeferredFillIn
excep\pfnDeferredFillIn(excep)
EndIf
If excep\bstrSource
SysFreeString_(excep\bstrSource)
EndIf
If excep\bstrDescription
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result, #True, PeekS(excep\bstrDescription, -1, #PB_Unicode))
CompilerEndIf
SysFreeString_(excep\bstrDescription)
Else
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, result, #True)
CompilerEndIf
EndIf
If excep\bstrHelpFile
SysFreeString_(excep\bstrHelpFile)
EndIf
EndIf
EndIf
ProcedureReturn result
EndProcedure
;/////////////////////////////////////////////////////////////////////////////////
;/////////////////////////////////////////////////////////////////////////////////
;The following function is called by all methods which need to invoke a COM method through iDispatch etc.
;It drills down through all the sub-objects of a method call as appropriate.
;Returns a HRESULT value; #S_OK for no errors.
Procedure.i COMateClass_INTERNAL_InvokePlus(*this._membersCOMateClass, invokeType, returnType, *ret.VARIANT, command$, *hStatement._COMatePLUSStatement)
Protected result.i = #S_OK, *statement._COMatePLUSStatement, subObjectIndex
Protected iDisp.iDispatch, var.VARIANT
;First job is to prepare a statement if one has not been provided by the developer.
If *hStatement
*statement = *hStatement
Else
result = COMate_INTERNAL_PrepareStatement(command$, @*statement)
EndIf
If *statement
VariantInit_(var)
iDisp = *this\iDisp
iDisp\AddRef() ;This seemingly extraneous AddRef() will be balanced (released) in the following loop or the code following the loop.
For subObjectIndex = 1 To *statement\numSubObjects-1
result = COMateClass_INTERNAL_InvokeiDispatch(*this, #DISPATCH_METHOD|#DISPATCH_PROPERTYGET, #VT_DISPATCH, var, iDisp, subObjectIndex, *statement)
iDisp\Release()
iDisp = var\pdispVal
If FAILED(result) Or iDisp = 0
Break
EndIf
VariantInit_(var)
Next
If SUCCEEDED(result)
If iDisp
result = COMateClass_INTERNAL_InvokeiDispatch(*this, invokeType, returnType, *ret, iDisp, *statement\numSubObjects, *statement)
iDisp\Release()
Else
;In this case we set an error with extra info.
CompilerIf Defined(COMATE_NOERRORREPORTING, #PB_Constant)=0
COMateClass_INTERNAL_SetError(*this, #S_FALSE, 0, "The '" + PeekS(*statement\methodName[subObjectIndex], -1, #PB_Unicode) + "' property returned a NULL object!")
CompilerEndIf
result = -1 ;This will ensure that the COMateClass_INTERNAL_SetError() does not reset the error.
EndIf
If SUCCEEDED(result)
;Sort out any return.
If *ret And *ret\vt <> returnType And returnType <> #VT_EMPTY
result = VariantChangeType_(*ret, *ret, 16, returnType)
EndIf
EndIf
EndIf
;Tidy up.
If *hStatement = 0
COMate_INTERNAL_FreeStatementHandle(*statement)
EndIf
EndIf
ProcedureReturn result
EndProcedure
;/////////////////////////////////////////////////////////////////////////////////
;///////////////////////////////////////////////////////////////////////////////////////////
;iDispatch errors will already have been processed.
Procedure COMateClass_INTERNAL_SetError(*this._membersCOMateClass, result, blnAllowDispError = 0, dispError$="")
Protected *error._COMateThreadErrors, Array.i, winError, len, *buffer
If COMate_gErrorTLS = 0 Or COMate_gErrorTLS = -1
;Create a new TLS index to hold error information.
COMate_gErrorTLS = TlsAlloc_()
EndIf
If COMate_gErrorTLS = -1 Or result = -1 Or (result = #DISP_E_EXCEPTION And blnAllowDispError = 0)
ProcedureReturn
EndIf
;Is there a TLS entry for this thread.
*error = TlsGetValue_(COMate_gErrorTLS)
If *error = 0 ;No existing entry.
;Attempt to allocate memory for a TLS entry for this thread.
*error = AllocateMemory(SizeOf(_COMateThreadErrors))
If *error
If TlsSetValue_(COMate_gErrorTLS, *error)
;Need to extend the memory if already allocated for the *COMate_gPtrThreadArray array so that the error memory can be freed later on.
Array = ReAllocateMemory(COMate_gPtrThreadArray, (COMate_gNumThreadElements+1)*SizeOf(Array))
If Array
COMate_gPtrThreadArray = Array
PokeI(COMate_gPtrThreadArray + COMate_gNumThreadElements*SizeOf(Array), *error)
COMate_gNumThreadElements+1
Else
TlsSetValue_(COMate_gErrorTLS, 0)
FreeMemory(*error)
*error = 0
EndIf
Else
FreeMemory(*error)
*error = 0
EndIf
EndIf
EndIf
If *error
*error\lastErrorCode = result
Select result
Case #S_OK
*error\lastError$ = "Okay."
Case #S_FALSE
If dispError$
*error\lastError$ = "The operation completed, but was only partially successful. (" + dispError$ + ")"
Else
*error\lastError$ = "The operation completed, but was only partially successful."
EndIf
Case #E_FAIL
*error\lastError$ = "Unspecified error."
Case #E_INVALIDARG
*error\lastError$ = "One or more arguments are invalid. Possibly a numerical overflow or too many nested objects, -if so, try splitting your method call into two or more subcalls."
Case #E_NOINTERFACE
*error\lastError$ = "Method is not implemented."
Case #E_OUTOFMEMORY
*error\lastError$ = "Problem allocating memory." + #CRLF$ + #CRLF$ + "(Possibly too many method arguments. Each method/property is limited by COMatePLUS to a maximum of " + Str(#COMate_MAXNUMVARIANTARGS) + " arguments.)"
Case #E_UNEXPECTED
*error\lastError$ = "An unexpected error."
Case #E_POINTER
*error\lastError$ = "An invalid pointer was supplied."
Case #E_NOTIMPL
*error\lastError$ = "Not implemented. In the case of attaching an event handler to a COM object, this could signify that the object does not provide any type information."
Case #CO_E_CLASSSTRING, #REGDB_E_CLASSNOTREG
*error\lastError$ = "Invalid progID/CLSID. Check your spelling of the programmatic identifier. Also check that the component / ActiveX control has been registered."
Case #CO_E_SERVER_EXEC_FAILURE
*error\lastError$ = "Server execution failed. Usually caused by an 'out of process server' timing out when asked to create an instance of a 'class factory'."
Case #DISP_E_TYPEMISMATCH
*error\lastError$ = "Type mismatch in the method parameters."
Case #TYPE_E_ELEMENTNOTFOUND
*error\lastError$ = "No type description was found in the library with the specified GUID whilst trying to create an event handler."
Case #CONNECT_E_ADVISELIMIT
*error\lastError$ = "Unable to set event handler because the connection point has already reached its limit of connections."
Case #CLASS_E_NOAGGREGATION
*error\lastError$ = "Class does not support aggregation (or class object is remote)."
Case #DISP_E_OVERFLOW
*error\lastError$ = "Overflow error whilst converting between types."
Case #DISP_E_UNKNOWNNAME
*error\lastError$ = "Method/property not supported by this object."
Case #DISP_E_BADPARAMCOUNT
*error\lastError$ = "Invalid number of method/property parameters."
Case #DISP_E_BADVARTYPE
*error\lastError$ = "A method/property parameter is not a valid (variant) type."
Case #DISP_E_MEMBERNOTFOUND
*error\lastError$ = "Member not found. (Check that you have not omitted any optional parameters and are not trying to set a read-only property etc.)"
Case #DISP_E_NOTACOLLECTION
*error\lastError$ = "Does not support a collection."
Case #E_ACCESSDENIED
*error\lastError$ = "A 'general' access denied error."
Case #RPC_E_WRONG_THREAD
*error\lastError$ = "The application called upon an interface that was marshalled for a different thread."
Case #DISP_E_EXCEPTION
*error\lastError$ = dispError$
If *error\lastError$ = ""
*error\lastError$ = "An exception occurred during the execution of this method/property."
EndIf
Default
;Check for a WIN32 facility code.
If *error\lastErrorCode & $7FFF0000 = $70000
winError = *error\lastErrorCode&$FFFF
len = FormatMessage_(#FORMAT_MESSAGE_ALLOCATE_BUFFER|#FORMAT_MESSAGE_FROM_SYSTEM, 0, winError, 0, @*Buffer, 0, 0)
If len
*error\lastError$ = "(FACILITY_WIN32 error " + Str(winError) + ") " + PeekS(*Buffer, len)
LocalFree_(*Buffer)
Else
*error\lastError$ = "(FACILITY_WIN32 error " + Str(winError) + ") Unable to retrieve error description from system!"
EndIf
Else
*error\lastError$ = "Unknown error. (Code : Hex " + Hex(*error\lastErrorCode, #PB_Long) + "). Please report this error code to the author at 'enquiries@nxsoftware.com'"
EndIf
EndSelect
EndIf
EndProcedure
;///////////////////////////////////////////////////////////////////////////////////////////
CompilerIf Defined(COMATE_NOINCLUDEATL, #PB_Constant)=0
;-OUTGOING 'SINK' INTERFACE METHODS.
;------------------------------------------------------------------------
;=================================================================================
;The QueryInterface() method of our COMate sink objects.
Procedure.i COMateSinkClass_QueryInterface(*this._COMateEventSink, *IID.IID, *Object.INTEGER)
If CompareMemory(*IID, ?IID_IUnknown, SizeOf(IID)) Or CompareMemory(*IID, ?IID_IDispatch, SizeOf(IID)) Or CompareMemory(*IID, @*this\connIID, SizeOf(IID))
*Object\i = *this
*this\refCount + 1
ProcedureReturn #S_OK
Else
*Object\i = 0
ProcedureReturn #E_NOINTERFACE
EndIf
EndProcedure
;=================================================================================
;=================================================================================
;The AddRef() method of our COMate sink objects.
Procedure.i COMateSinkClass_AddRef(*this._COMateEventSink)
*this\refCount + 1
ProcedureReturn *this\refCount
EndProcedure
;=================================================================================
;=================================================================================
;The Release() method of our COMate sink objects.
Procedure.i COMateSinkClass_Release(*this._COMateEventSink)
*this\refCount - 1
If *this\refCount = 0
If *this\parent
;Release all event related window properties added to the ActiveX container.
If IsWindow_(*this\parent\hWnd)
EnumPropsEx_(*this\parent\hWnd, @COMate_DelSinkPropsCallback(),#Null)
EndIf
*this\parent\eventSink = 0
EndIf
*this\typeInfo\Release()
FreeMemory(*this)
ProcedureReturn 0
Else
ProcedureReturn *this\refCount
EndIf
EndProcedure
;=================================================================================
;=================================================================================
;The next 3 methods of the COMate sink interface are possibly not required, but ...
Procedure.i COMateSinkClass_GetTypeInfoCount(*this._COMateEventSink, *pctinfo.INTEGER)
*pctinfo\i = 1
ProcedureReturn #S_OK
EndProcedure
Procedure.i COMateSinkClass_GetTypeInfo(*this._COMateEventSink, iTInfo, lcid, *ppTInfo.INTEGER)
*ppTInfo\i = *this\typeInfo
*this\typeInfo\AddRef()
ProcedureReturn #S_OK
EndProcedure
Procedure.i COMateSinkClass_GetIDsOfNames(*this._COMateEventSink, *riid, *rgszNames, *cNames, lcid, *DispID)
ProcedureReturn DispGetIDsOfNames_(*this\typeInfo, *rgszNames, *cNames, *DispID)
EndProcedure
;=================================================================================
;=================================================================================
;The Invoke() method of our COMate sink objects.
;This is where we call the user's event procedure.
Procedure.i COMateSinkClass_Invoke(*this._COMateEventSink, dispid, *riid, lcid, wflags.w, *Params.DISPPARAMS, *Result.VARIANT, *pExept, *ArgErr)
Protected result.i = #S_OK, bstrName.i, nameCount, tempParams, eventName$, returnType, address
Protected callbackNoReturn.COMate_EventCallback_NORETURN, callbackIntegerReturn.COMate_EventCallback_INTEGERRETURN, callbackRealReturn.COMate_EventCallback_REALRETURN, callbackStringReturn.COMate_EventCallback_STRINGRETURN, callbackUnknownReturn.COMate_EventCallback_UNKNOWNRETURN
Protected intRet.q, realRet.d, stringRet$
result = *this\TypeInfo\GetNames(dispid, @bstrName, 1, @nameCount)
If SUCCEEDED(result)
If bstrName
tempParams = *this\dispParams
*this\dispParams = *Params
eventName$ = PeekS(bstrName, -1, #PB_Unicode)
SysFreeString_(bstrName)
;Call the 'global' #COMate_CatchAllEvents handler if defined.
If *this\callback
If *this\returnType = #COMate_OtherReturn
callbackUnknownReturn = *this\callback
callbackUnknownReturn(*this\parent, eventName$, *Params\cArgs + *Params\cNamedArgs, *Result)
Else
*this\callback(*this\parent, eventName$, *Params\cArgs + *Params\cNamedArgs)
EndIf
EndIf
;Call any individual handler attached to this event. We need to take into account the return type.
If *this\parent\hWnd
address = GetProp_(*this\parent\hWnd, eventName$ + "_COMate")
If address
returnType = GetProp_(*this\parent\hWnd, eventName$ + "_RETURN_COMate")
Select returnType
Case #COMate_NoReturn
callbackNoReturn = address
callbackNoReturn(*this\parent, eventName$, *Params\cArgs + *Params\cNamedArgs)
Case #COMate_IntegerReturn
callbackIntegerReturn = address
intRet = callbackIntegerReturn(*this\parent, eventName$, *Params\cArgs + *Params\cNamedArgs)
If *Result
*Result\vt = #VT_I8
*Result\llVal = intRet
EndIf
Case #COMate_RealReturn
callbackRealReturn = address
realRet = callbackRealReturn(*this\parent, eventName$, *Params\cArgs + *Params\cNamedArgs)
If *Result
*Result\vt = #VT_R8
*Result\dblVal = realRet
EndIf
Case #COMate_StringReturn
callbackStringReturn = address
stringRet$ = callbackStringReturn(*this\parent, eventName$, *Params\cArgs + *Params\cNamedArgs)
If *Result
*Result\vt = #VT_BSTR
*Result\bstrVal = COMate_MakeBSTR(stringRet$)
EndIf
Case #COMate_OtherReturn
callbackUnknownReturn = address
callbackUnknownReturn(*this\parent, eventName$, *Params\cArgs + *Params\cNamedArgs, *Result)
EndSelect
EndIf
EndIf
*this\dispParams = tempParams
Else
result = #E_OUTOFMEMORY
EndIf
EndIf
ProcedureReturn result
EndProcedure
;=================================================================================