|
-
Dec 1st, 2014, 12:43 PM
#11
Re: [VB6] Call Functions By Pointer (Universall DLL Calls)
Bonnie, if this is a Sub from a VB class you want to call, and you know the method's name, far easier:
:: Copy pointer to uninitialized object and call the Object.SubName, remove pointer from that object, done
Now, for a bit of fun, let's do a simple example of calling the object's IDispatch.Invoke method instead
1) Create new class, leave default name & add these 2 methods
Code:
Public Sub TestMe()
MsgBox "Test"
End Sub
Public Function TestMe2() As Long
MsgBox "Test2" & vbNewLine & "vRtn should equal " & ObjPtr(Me)
TestMe2 = ObjPtr(Me)
End Function
2) Within the form add a command button & this sample code. Ensure project also includes the attached cUniversalDLLCalls class
Code:
Private Type DISPPARAMS
rgVarg As Long ' pointer to array of variant agruments
rgDispID As Long ' pointer to array of DISPIDs of named argument
cArgs As Long ' number of arguments
cNamedArgs As Long ' number of named arguments
End Type
' sample of getting the IDataObject GUID
Private Sub Command1_Click()
Const IUnknownRelease As Long = 8&
Const IDispatchIDsOfNames As Long = 20&
Const IDispatchInvoke As Long = 24&
Dim IID_Dispatch As Long, lDispID As Long
Dim aGUID(0 To 3) As Long, sName As String
Dim DP As DISPPARAMS, vRtn As Variant
Dim c As New cUniversalDLLCalls, tc As Class1
Set tc = New Class1
sName = "{00020400-0000-0000-C000-000000000046}" ' GUID for IDispatch
c.CallFunction_DLL "ole32.dll", "IIDFromString", STR_NONE, CR_LONG, CC_STDCALL, StrPtr(sName), VarPtr(aGUID(0))
' ensure object implements IDispatch...
c.CallFunction_COM ObjPtr(tc), 0&, CR_LONG, CC_STDCALL, VarPtr(aGUID(0)), VarPtr(IID_Dispatch)
If IID_Dispatch <> 0& Then
Erase aGUID()
sName = "TestMe2" ' << change to another known method in the class
Call c.CallFunction_COM(IID_Dispatch, IDispatchIDsOfNames, CR_LONG, CC_STDCALL, VarPtr(aGUID(0)), VarPtr(sName), 1&, 0&, VarPtr(lDispID))
If lDispID <> 0 Then
Call c.CallFunction_COM(IID_Dispatch, IDispatchInvoke, CR_LONG, CC_STDCALL, lDispID, VarPtr(aGUID(0)), 0&, 1&, VarPtr(DP), VarPtr(vRtn), 0&, 0&)
Debug.Print "return value if any: "; vRtn
Else
Debug.Print "method ["; sName; "] not found"
End If
' Release the interface at some point. QueryInterface calls AddRef internally
c.CallFunction_COM IID_Dispatch, IUnknownRelease, CR_LONG, CC_STDCALL
End If
End Sub
change sName = "TestMe2" to sName = "TestMe" to test the other sub. Note: that if parameters exist, then this gets more difficult and the DISPPARAMS structure needs to be filled out completely
Edited: If wanting to pursue this further, here's a bit of help with the DISPPARAMS structure
1) declare a variable vArgs() As Variant, size it to the number of parameters the method expects
2) populate it in reverse order and ensure the correct vartype of each param, example:
method's params: (Index As Integer, NewValue As Long)
NewValue would be: vArgs(0) = 15&, CLng(15), just ensure Long vartype
Index would be: vArgs(1) = 1%, CInt(1), just ensure Integer vartype 3) the structure members: DP.cArgs = nrArgs: DP.rgVarg = VarPtr(vArgs(0))
4) The 3rd from last parameter in that IDispatchInvoke sample call above is 1&. That is the constant for DISPATCH_METHOD, aka, vbMethod
So how do we NOT pass an optional parameter to a VB class? Per docs, we must pass Variant of type Error with a specific error code in place of the optional parameter. How that parameter is coded determines whether VB sees it as passed or not passed. The following shows how to NOT provide the final Optional parameter of a method. Remember last parameter is 1st in the array:
Code:
Const DISP_E_PARAMNOTFOUND As Long = &H80020004
vArgs(0) = DISP_E_PARAMNOTFOUND
' tweak to force variant type of Error. Can use CopyMemory API if desired, but...
c.CallFunction_DLL "kernel32.dll", "RtlMoveMemory", STR_NONE, CR_None, CC_STDCALL, _
VarPtr(vArgs(0)), VarPtr(vbError), 2&
' Note: Proper way would be to have VB give us a Variant of Error vartype, i.e.,
' vArgs(0)= CVErr(DISP_E_PARAMNOTFOUND) but that errors
Ok, that was fun. If anyone is asking what's difference between a parameter and a named parameter? See this MSDN link. If you wish to use named parameters/arguments with IDispatch, you have to get the DISPID of each named argument when you made the call to DispatchIDsOfNames, passing a array of names & a return array for the DISPIDs. These are then populated separately in the DISPPARAMS members. Here is a bit more info. Bottom line, more work but doable.
Last but not least, haven't tried messing with methods that take ParamArrays, I'll let someone else play with that if they wish
Last edited by LaVolpe; Dec 1st, 2014 at 06:43 PM.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|