Also, when you wish to execute a function in a dll using the Declare function, you can only call functions created by the stdcall calling convention.
These constraints can be avoided by using the DispCallFunc API.
The DispCallFunc is widely used in VB6 when erasing the history of IE.
Although the DispCallFunc is known as API for calling the IUnknown interface, in fact, you can also perform other functions other than COM by passing the NULL to the first argument.
As explained in the http://msdn.microsoft.com/en-us/library/ms221473(v=vs.85).aspx , the DispCallFunc argument is as follows.
HRESULT DispCallFunc( void *pvInstance, ULONG_PTR oVft, CALLCONV cc, VARTYPE vtReturn, UINT cActuals, VARTYPE *prgvt, VARIANTARG **prgpvarg, VARIANT *pvargResult );
Use of the argument sixth and seventh is very peculiar, and there are few descriptions about it available. Therefore, I'll try to summarize it this time around.
First argument, pvInstance
In this time, I am going to call functions other than COM. Thus, always setting at 0.
Second argument, oVft
Passing the address of a function.
Third argument, cc
Passing the function's calling convention.
For CALLCONV type, the following values are defined for the OAIdl.h.
enum tagCALLCONV { CC_FASTCALL = 0, CC_CDECL = 1, CC_MSCPASCAL = CC_CDECL + 1, CC_PASCAL = CC_MSCPASCAL, CC_MACPASCAL = CC_PASCAL + 1, CC_STDCALL = CC_MACPASCAL + 1, CC_FPFASTCALL = CC_STDCALL + 1, CC_SYSCALL = CC_FPFASTCALL + 1, CC_MPWCDECL = CC_SYSCALL + 1, CC_MPWPASCAL = CC_MPWCDECL + 1, CC_MAX = CC_MPWPASCAL + 1 } CALLCONV;
For a general WIN32API function, CC_STDCALL (= 4) is used.
For a function that is specifically designed for language C, CC_CDECL (= 1) is used.
Fourth argument, vtReturn
Setting the value of VbVarType enumerated type that indicates the type of the function's return value.
In the event where the function does not return a value (the Sub procedure referred to in VB), setting the vbEmpty.
VARTYPE type as defined in WTypes.h is shown below.
typedef unsigned short VARTYPE;
By ignoring 'unsigned', it is equivalent to the Integer type in VB.
Fifth argument, cActuals
Setting the number of function arguments.
Sixth argument, prgvt
Storing in an array of Integer type VbVarType enumerated values that indicate the type of the function's arguments, and then passing the address of the beginning of the array.
However, when the number of the arguments is 0, passing 0.
Seventh argument, prgpvarg
Once again, storing each function's argument in Variant type, and storing the address of each of the variant variable in the Long type array that is prepared separately, then passing the address of the beginning of the array.
However, when the number of arguments is 0, then passing 0.
Then, the return value of the DispCallFunc API is, if a call to the function is done successfully in the Long type, S_OK (= 0) is returned.
As an example, the API declaration and the declaration of the enumeration in VB are as follows.
Private Declare PtrSafe Function DispCallFunc Lib "OleAut32.dll" _
(ByVal pvInstance As LongPtr, _
ByVal oVft As LongPtr, _
ByVal cc As Long, _
ByVal vtReturn As Integer, _
ByVal cActuals As Long, _
ByVal prgvt As LongPtr, _
ByVal prgpvarg As LongPtr, _
ByVal pvargResult As LongPtr) As Long
Enum tagCALLCONV
CC_FASTCALL = 0
CC_CDECL = 1
CC_MSCPASCAL = CC_CDECL + 1
CC_PASCAL = CC_MSCPASCAL
CC_MACPASCAL = CC_PASCAL + 1
CC_STDCALL = CC_MACPASCAL + 1
CC_FPFASTCALL = CC_STDCALL + 1
CC_SYSCALL = CC_FPFASTCALL + 1
CC_MPWCDECL = CC_SYSCALL + 1
CC_MPWPASCAL = CC_MPWCDECL + 1
CC_MAX = CC_MPWPASCAL
End Enum
First, I tried the simplest way with a pointer to a function with no defined arguments in a standard module of VBA.
In VBA, as long as functions that are defined in a standard module, you can obtain those addresses using AddressOf operator.
The source code on this page needs to be pasted into the standard module. If you paste it into Sheet1 or ThisWorkBook, a compile error will occur.
Example.1
'You can use a sample code for this site freely.
'Though this is not a duty, I am grateful that you describe that you reffered this site
'(https://akihitoyamashiro.blogspot.com/2020/07/how-to-use-function-pointer-in-vba-1.html),
'when you present this sample code in your web site.
Private Declare PtrSafe Function DispCallFunc Lib "OleAut32.dll" _
(ByVal pvInstance As LongPtr, _
ByVal oVft As LongPtr, _
ByVal cc As Long, _
ByVal vtReturn As Integer, _
ByVal cActuals As Long, _
ByVal prgvt As LongPtr, _
ByVal prgpvarg As LongPtr, _
ByVal pvargResult As LongPtr) As Long
Enum tagCALLCONV
CC_FASTCALL = 0
CC_CDECL = 1
CC_MSCPASCAL = CC_CDECL + 1
CC_PASCAL = CC_MSCPASCAL
CC_MACPASCAL = CC_PASCAL + 1
CC_STDCALL = CC_MACPASCAL + 1
CC_FPFASTCALL = CC_STDCALL + 1
CC_SYSCALL = CC_FPFASTCALL + 1
CC_MPWCDECL = CC_SYSCALL + 1
CC_MPWPASCAL = CC_MPWCDECL + 1
CC_MAX = CC_MPWPASCAL
End Enum
Public Sub NoParamNoReturn()
MsgBox "NoParamNoReturn is called."
End Sub
Public Sub Test1()
Dim lDispCallFuncResult As Long
'Declare a Variant variable that will store the return value of the function called.
'This variable is needed even if the function does not return a value.
Dim vFuncResult As Variant
'In this case, there is no need to initialize the variable after declaring it.
'However, when using a Variant variable, it can be initialized like this
'by setting it to Empty.
vFuncResult = Empty
'You can get the address of a function in VBA by using the AddressOf operator.
'Enter the name of the enumerated type; at the point when you type in the period
'you will be able to use VBA's input support functionality.
'This time we are calling a sub procedure so there is no value to be returned.
'Hence we set the data type of the return variable to be vbEmpty(=0).
lDispCallFuncResult = DispCallFunc(0, AddressOf NoParamNoReturn, _
tagCALLCONV.CC_STDCALL, VbVarType.vbEmpty, _
0, 0, 0, VarPtr(vFuncResult))
'When the call to the DispCallFunc API is successful, it returns 0.
'However, this only means that the call to the DispCallFunc API was successful,
'and it does not indicate whether the target function call was successful or not.
Debug.Print lDispCallFuncResult
End Sub
In the next section we also try using pointers of functions defined in VBA's standard module.
However, in the next function there are arguments and return value.
No comments:
Post a Comment