-
Jul 16th, 2017, 09:36 PM
#1
Thread Starter
Fanatic Member
[RESOLVED] DispCallFunc to call COM Property/Method
Hi VB experts,
I am trying to retrieve the Caption of a newly created Excel Application using the following code but I can't make it work .. The varRtn argument returns Empty :
Code:
Private Declare Function DispCallFunc Lib "OleAut32.dll" ( _
ByVal pvInstance As Long, _
ByVal oVft As Long, _
ByVal cc_ As Long, _
ByVal vtReturn As Integer, _
ByVal cActuals As Long, _
ByRef prgvt As Integer, _
ByRef prgpvarg As Long, _
ByRef pvargResult As Variant _
) As Long
Sub Test()
Const CC_STDCALL As Long = 4&
Dim XL_App As Object
Dim pUNK As Long
Dim IID_First_Method_Offset As Long
Dim varRtn As Variant
Set XL_App = CreateObject("Excel.Application")
pUNK = ObjPtr(XL_App)
IID_First_Method_Offset = 77 * 4 '<== Excel Caption Property_Get index=77
Call DispCallFunc(pUNK, IID_First_Method_Offset, CC_STDCALL, _
VBA.vbString, 0&, 0&, 0&, varRtn)
MsgBox varRtn
End Sub
I hope someone can shed some light on this.
Regards.
-
Jul 17th, 2017, 06:24 AM
#2
Re: DispCallFunc to call COM Property/Method
Originally Posted by JAAFAR
Hi VB experts,
I am trying to retrieve the Caption of a newly created Excel Application using the following code but I can't make it work ..
The DispCallFunc returns a value if it fails. You should be looking at the error, it may shed some light. Ensure you are correct with the VTable offset of 77*4. Additionally, typically you do not return strings without first sizing the string. There may be another function you need to call to get that string size?
If you can't resolve this on your own, you may want to post the function/method that lives at offset 77*4. Preferably post the link to that method or provide all the details for each parameter, if any, along with the details of the function.
Edited: Note that your VTable offset must include any VTables/interfaces that your target interface inherits from, i.e., all inherit at least IUnknown and its 3 methods. But as a side note, doesn't the Excel app itself, give you functions/properties for that information? Just curious.
Last edited by LaVolpe; Jul 17th, 2017 at 07:09 AM.
-
Jul 17th, 2017, 10:28 AM
#3
Thread Starter
Fanatic Member
Re: DispCallFunc to call COM Property/Method
@LaVolpe
Here is a snapshot from a TypeLib Browser of the VTable for Excel Function that lives @ that offset along with details of its parameters :
Here is the TypeLib Listing :
Code:
' ****************************************************************************************
' [get_]Caption property
' Interface name = _Application
' VTable offset = 308 [&H134]
' DispID = 139 [&H0000008B]
' ****************************************************************************************
FUNCTION Excel_Application_get_Caption ( _
BYVAL pthis AS DWORD PTR _ ' %VT_DISPATCH <dispinterface>
, BYREF RHS AS STRING _ ' *%VT_BSTR <DYNAMIC UNICODE STRING> [out]
) AS LONG ' %VT_HRESULT <LONG>
LOCAL HRESULT AS LONG
IF pthis = 0 THEN FUNCTION = %E_POINTER : EXIT FUNCTION
CALL DWORD @@pthis[77] USING Excel_Application_get_Caption(pthis, RHS) TO HRESULT
FUNCTION = HRESULT
END FUNCTION
' ****************************************************************************************
My VB code:
Code:
Private Declare Function DispCallFunc Lib "OleAut32.dll" ( _
ByVal pvInstance As Long, _
ByVal oVft As Long, _
ByVal cc_ As Long, _
ByVal vtReturn As Integer, _
ByVal cActuals As Long, _
ByRef prgvt As Integer, _
ByRef prgpvarg As Long, _
ByRef pvargResult As Variant _
) As Long
Private Sub Test()
Dim app As Object
Dim lRet As Long
Const CC_STDCALL As Long = 4&
Dim pUNK As Long
Dim IID_First_Method_Offset As Long, varRtn As Variant
Set app = CreateObject("Excel.Application")
pUNK = ObjPtr(app)
IID_First_Method_Offset = 77 * 4
lRet = DispCallFunc(pUNK, IID_First_Method_Offset, CC_STDCALL, _
vbString, 0&, 0&, 0&, varRtn)
Debug.Print Hex(lRet)
Debug.Print TypeName(varRtn)
End Sub
The error returned is &H80004003 I looked up this error code and turned out to be : E_POINTER (Pointer that is not valid)
The variant varRtn returns Empty
But as a side note, doesn't the Excel app itself, give you functions/properties for that information? Just curious.
Yes I know but my objective is to learn how to use this handy DispCallFunc API and I like to start with simple examples with COM Interfaces that I am familiar with then, after that, gradually take on more complicated interfaces.
Regards.
Last edited by JAAFAR; Jul 17th, 2017 at 10:44 AM.
-
Jul 17th, 2017, 11:08 AM
#4
Hyperactive Member
Re: DispCallFunc to call COM Property/Method
are u attempting to extend excel's inner methods, events or interface?
-
Jul 17th, 2017, 11:19 AM
#5
Re: DispCallFunc to call COM Property/Method
Something doesn't seem right. You're creating an Excel.Application, and then assigning it to an IDispatch (Object).
And then calling a VTable entry on Excel.Application. This would only work if _Application derives from IDispatch, and it returns the same pointer as a
QueryInterface for _Application.
If you're going to call VTable pointers directly - don't mix it with late binding. You have no guarantee what interface you're even calling.
-
Jul 17th, 2017, 11:24 AM
#6
Thread Starter
Fanatic Member
Re: DispCallFunc to call COM Property/Method
When I change the code line from late binding :
Code:
Set app = CreateObject("Excel.Application")
to early binding :
Code:
Set app = New Excel.Application
The code crashes and shuts down
-
Jul 17th, 2017, 11:52 AM
#7
Re: DispCallFunc to call COM Property/Method
What typelib are you referencing, the EXE itself?
Also the return type is always an HRESULT / long.
If you're getting a property value back - you have to pass a pointer to the return value as the last parameter.
Last edited by DEXWERX; Jul 17th, 2017 at 11:57 AM.
-
Jul 17th, 2017, 12:14 PM
#8
Thread Starter
Fanatic Member
Re: DispCallFunc to call COM Property/Method
What typelib are you referencing, the EXE itself?
The excel exe as I want to retrieve the Caption Property of the Excel Application.
If you're getting a property value back - you have to pass a pointer to the return value as the last parameter.
Not sure how to go about that ... I just hope someone can show me the VB code for how this can be done.
-
Jul 17th, 2017, 12:37 PM
#9
Re: DispCallFunc to call COM Property/Method
Here's a thread I started awhile back regarding the DispCallFunc API. Sorry, not on a system where I can play along.
-
Jul 17th, 2017, 12:38 PM
#10
Re: DispCallFunc to call COM Property/Method
if you're crashing that could mean you're close. It's probably an access violation with how your are passing the return variable ptr.
-
Jul 17th, 2017, 01:29 PM
#11
Thread Starter
Fanatic Member
Re: DispCallFunc to call COM Property/Method
Retrieving the Caption Property of the Excel.Application interface via the use of the DispCallFunc API seems to me a fair enough request and "what should be" a good way of demonstrating for beginners how this API can be used in VB for calling COM Interfaces functions.
I have googled around but I have only seen this function used in VB examples for calling Interface Functions/Methods that are not exposed to VB the calssic way.
-
Jul 17th, 2017, 02:35 PM
#12
Re: DispCallFunc to call COM Property/Method
As an alternative, here's how I'd do this. It'll work regardless of whether or not you're the one who loaded Excel.
Here's the code I pulled together to do it (for a BAS module):
Code:
Option Explicit
'
Private Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, lpdwProcessId As Long) As Long
Private Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hWnd As Long) As Long
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare Function GetDesktopWindow Lib "user32" () As Long
'
Dim hWndCount As Long
Dim hWnds() As Long
Dim lTheProcessIdMatch As Long
'
Public Function hWndOfChildFromPartTitle(sPartTitle As String, Optional hWndParent As Long, Optional iBackTo As Long = 1) As Long
' Returns the first child with a matching title name.
'
' iBackTo can be used to fetch a window other than the last one loaded.
'
Dim hWnd() As Long
Dim i As Long
Dim j As Long
'
If hWndParent = 0 Then hWndParent = GetDesktopWindow
'
hWnd = hWndOfAllChildWindows(hWndParent)
If IsDimmedLng(hWnd) Then
j = 1
For i = LBound(hWnd) To UBound(hWnd)
If InStr(WindowText(hWnd(i)), sPartTitle) <> 0 Then
If iBackTo = j Then
hWndOfChildFromPartTitle = hWnd(i)
Exit Function
Else
j = j + 1
End If
End If
Next i
End If
End Function
Public Function WindowText(hWndOfInterest As Long) As String
' Form or control.
Dim s As String
Dim l As Long
'
l = GetWindowTextLength(hWndOfInterest)
s = Space$(l + 1)
l = GetWindowText(hWndOfInterest, s, l + 1)
s = RTrimNull(s)
WindowText = Trim$(s)
End Function
Public Function hWndOfAllChildWindows(hWndParent As Long) As Long()
' See note for above function. This is same except it's for child windows.
' The hWndOfNextChildWindow can also be used, but this is more reliable.
hWndCount = 0
ReDim hWnds(1 To 100)
EnumChildWindows hWndParent, AddressOf EnumWindowsCallBack, &H0 ' Doesn't return until done.
If hWndCount > 0 Then
ReDim Preserve hWnds(1 To hWndCount)
Else
Erase hWnds
End If
'
hWndOfAllChildWindows = hWnds
End Function
Public Function EnumWindowsCallBack(ByVal hWnd As Long, ByVal lpData As Long) As Long
' Only the API calls this. It should not be called by user.
'
' This callback function is called by Windows (from the EnumWindows
' API call) for EVERY window that exists. It populates the aWindowList
' array with a list of windows that we are interested in.
'
EnumWindowsCallBack = 1
'
If lTheProcessIdMatch = 0 Or ProcessId(hWnd) = lTheProcessIdMatch Then
hWndCount = hWndCount + 1
If UBound(hWnds) < hWndCount Then ReDim Preserve hWnds(1 To UBound(hWnds) + 100)
hWnds(hWndCount) = hWnd
End If
End Function
Public Function ProcessId(hWndOfInterest As Long) As Long
' This process ID is unique to the entire application to which the window belongs.
' A process ID will always be unique for each running copy of an application, even if more than one copy is running.
Dim lProcId As Long
Call GetWindowThreadProcessId(hWndOfInterest, lProcId)
ProcessId = lProcId
End Function
Public Function IsDimmedLng(TheArray() As Long) As Boolean
' This won't fail on one of the (0 to -1) arrays.
On Error Resume Next
IsDimmedLng = (LBound(TheArray) = LBound(TheArray)) ' Will error (leaving IsDimmedLng = False) if not dimensioned.
On Error GoTo 0
End Function
Public Function RTrimNull(s As String) As String
Dim i As Integer
i = InStr(s, vbNullChar)
If i Then
RTrimNull = Left$(s, i - 1)
Else
RTrimNull = s
End If
End Function
And here's some code I threw into a Form1 to do a bit of testing (just two CommandButtons):
Code:
Option Explicit
Private Sub Command1_Click()
Dim h As Long
h = hWndOfChildFromPartTitle("Excel")
If h Then MsgBox WindowText(h)
End Sub
Private Sub Command2_Click()
Dim h As Long
h = hWndOfChildFromPartTitle("Excel", , 2)
If h Then MsgBox WindowText(h)
End Sub
Enjoy,
Elroy
EDIT1: You could pull that ProcessID match stuff out of it if you wanted. That was just in there from needing it for something else. Also, there probably is an easier way to do all of this, but this certainly gets it done.
EDIT2: This is actually in response to post #13 (didn't want to make it another post). Very good, JAAFAR. In that case, I'll bow out and continue observing. I was just hoping to get you on your way in the event you were trying to do something more specific. Good Luck.
Last edited by Elroy; Jul 17th, 2017 at 03:02 PM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Jul 17th, 2017, 02:57 PM
#13
Thread Starter
Fanatic Member
Re: DispCallFunc to call COM Property/Method
Hi Elroy,
I Know how to retrieve the excel application caption either by using the Application.Caption Property directly or by using the GetWindowText API like you have shown in your code but that is not what I want.
What I want is to see the use of the DispCallFunc API for achieving this goal in order to learn how one could call a COM Interface function just by providing a pointer to the queried interface and the VTable index of the function and without the need of a TLB.
Thank you.
-
Jul 17th, 2017, 03:15 PM
#14
Re: DispCallFunc to call COM Property/Method
Just a bit of research for you. Looking around on the web, it appears the _Application is a co-class of Application interface. As mentioned earlier by DEXWERX, you will need to ensure the ObjPtr() you retrieved is to the right interface. This is typically ensured by calling IUnknown:QueryInterface and passing the GUID of the desired interface. If the Object @ ObjPtr() implements that interface, then a reference to it will be returned. You would call your VTable method and release the interface. In post #9, I referenced a thread that shows the API usage with several examples. In that thread, specifically post #2, is an example of how to query the object to see if it supports the desired interface (GUID).
-
Jul 17th, 2017, 03:25 PM
#15
Thread Starter
Fanatic Member
Re: DispCallFunc to call COM Property/Method
@LaVolpe
The TypeLib Browser (2.05 By Jose Roca) that I am using shows _Application as a Dual-Interface and states "Inherited Interface =IDispatch' if that helps .
$IID__Application = GUID$("{000208D5-0000-0000-C000-000000000046}")
Am I looking in the right place ?
-
Jul 17th, 2017, 03:39 PM
#16
Re: DispCallFunc to call COM Property/Method
That looks promising. You can query ObjPtr(app) and see if that interface is implemented. Post #2 in that link I gave you shows how to query it. What is interesting is the inherits information. 77*4 is the vtable offset you want to use, but does that include the 4 members of IDispatch & the 3 members of IUnknown? You can validate that I think by simply counting the number of members on _Application until you get to Caption:Get (assuming the 1st member is not from IUnknown else 77 is likely correct). If the number is 77, then IDispatch & IUnknown are probably not calculated in the offset, but I didn't write that typlib viewer so I don't know if Roca is taking into account inherited interfaces when giving you the offset.
But step 1 is always to ensure you get a reference to the correct interface. You picked a more complicated interface to begin this journey . This is one reason why in that other thread, I picked relatively easier examples... build on it and move forward
-
Jul 17th, 2017, 03:42 PM
#17
Re: DispCallFunc to call COM Property/Method
if you use early binding, you guarrantee a matching interface.
I have however tested that at least with Excel 2000 it returns the same interface for IDispatch as _Application.
And retrieving the caption via DispCallFunc (oVft=(77*4) does work well, I just don't have time to extract the code from my project.
You'd have better luck using code from Schmidt or Lavolpe in the mean time.
http://www.vbforums.com/showthread.p...=1#post4902281
Olaf has a rapper function called vtblCall that works using this method.
Code:
hr = vtblCall(vbLong, ObjPtr(b), 77, VarPtr(MyCaption))
-
Jul 17th, 2017, 03:48 PM
#18
Thread Starter
Fanatic Member
Re: DispCallFunc to call COM Property/Method
Originally Posted by LaVolpe
That looks promising. You can query ObjPtr(app) and see if that interface is implemented. Post #2 in that link I gave you shows how to query it. What is interesting is the inherits information. 77*4 is the vtable offset you want to use, but does that include the 4 members of IDispatch & the 3 members of IUnknown? You can validate that I think by simply counting the number of members on _Application until you get to Caption:Get (assuming the 1st member is not from IUnknown else 77 is likely correct). If the number is 77, then IDispatch & IUnknown are probably not calculated in the offset, but I didn't write that typlib viewer so I don't know if Roca is taking into account inherited interfaces when giving you the offset.
But step 1 is always to ensure you get a reference to the correct interface. You picked a more complicated interface to begin this journey . This is one reason why in that other thread, I picked relatively easier examples... build on it and move forward
It does take into account the the 4 members of IDispatch & the 3 members of IUnknown as the first Function in the list has OFFSET # 28
-
Jul 17th, 2017, 03:52 PM
#19
Thread Starter
Fanatic Member
Re: DispCallFunc to call COM Property/Method
Originally Posted by DEXWERX
if you use early binding, you guarrantee a matching interface.
I have however tested that at least with Excel 2000 it returns the same interface for IDispatch as _Application.
And retrieving the caption via DispCallFunc (oVft=(77*4) does work well, I just don't have time to extract the code from my project.
You'd have better luck using code from Schmidt or Lavolpe in the mean time.
http://www.vbforums.com/showthread.p...=1#post4902281
Olaf has a rapper function called vtblCall that works using this method.
Code:
hr = vtblCall(vbLong, ObjPtr(b), 77, VarPtr(MyCaption))
Well, the code I posted doesn't work ! If you could show me the code that worked for you it would be great !
I'll take a look @ the link and get back later .. Thanks
-
Jul 17th, 2017, 03:52 PM
#20
Re: DispCallFunc to call COM Property/Method
Originally Posted by JAAFAR
It does take into account the the 4 members of IDispatch & the 3 members of IUnknown as the first Function in the list has OFFSET # 28
:thumbs-up:
-
Jul 17th, 2017, 04:17 PM
#21
Re: DispCallFunc to call COM Property/Method
Using the class in that other thread, copied and pasted into your test project, the following appears to work? If so, then simply use the class as a template for further testing or to rewrite your own generic function for the API.
1. Name that class, after pasting, as: cDispCall
2. Paste this code in your button click event
Code:
Private Sub Command1_Click()
' sample of getting the IDataObject GUID
Const IUnknownQueryInterface As Long = 0& ' IUnknown vTable offset to Query implemented interfaces
Const IUnknownRelease As Long = 8& ' IUnkownn vTable offset to decrement reference count
Dim XL_App As Object, pUNK As Long
Dim IID_Application As Long
Dim IID_First_Method_Offset As Long
Dim aGUID(0 To 3) As Long
Dim c As cDispCall
Dim sCaption As String
Set XL_App = CreateObject("Excel.Application")
pUNK = ObjPtr(XL_App)
Set c = New cDispCall
c.CallFunction_DLL "ole32.dll", "IIDFromString", STR_NONE, CR_LONG, CC_STDCALL, StrPtr("{000208D5-0000-0000-C000-000000000046}"), VarPtr(aGUID(0))
c.CallFunction_COM pUNK, IUnknownQueryInterface, CR_LONG, CC_STDCALL, VarPtr(aGUID(0)), VarPtr(IID_Application)
If IID_Application <> 0& Then
IID_First_Method_Offset = 77 * 4 '<== Excel Caption Property_Get index=77
c.CallFunction_COM IID_Application, IID_First_Method_Offset, CR_LONG, CC_STDCALL, VarPtr(sCaption)
c.CallFunction_COM IID_Application, IUnknownRelease, CR_LONG, CC_STDCALL
End If
End Sub
When run, sCaption variable should contain the caption. I'm thinking that property get statements are not returning a value, i.e., VOID. Therefore, you could change
this: c.CallFunction_COM IID_Application, IID_First_Method_Offset, CR_LONG, CC_STDCALL, VarPtr(sCaption)
to: c.CallFunction_COM IID_Application, IID_First_Method_Offset, CR_None, CC_STDCALL, VarPtr(sCaption)
Edited: COM methods (vs. some APIs) always uses BSTR for strings (unicode compatible). When the method asks for a pointer to a string, pass StrPtr(theString). When it asks for a far pointer PTR>PTR, then pass VarPtr(theString). Both examples are shown above.
Last edited by LaVolpe; Jul 17th, 2017 at 04:31 PM.
-
Jul 17th, 2017, 05:24 PM
#22
Thread Starter
Fanatic Member
Re: DispCallFunc to call COM Property/Method
@LaVolpe
Thanks for the code ... It is going to take me sometime to understand/make work the Class code for two reasons 1) Because of the abstract extra layer of the Wrapping class which adds more complexity 2) Because I use VBA/Win64 and therefore the size of Pointers and the memory addresses will need to be amended to work for 64bit memory layout
-
Jul 17th, 2017, 05:30 PM
#23
Re: DispCallFunc to call COM Property/Method
Well, that part I can't help you with -- don't use 64bit O/S. However, there are plenty on this site that do. If its VBA questions from this point on, you'll want to post those in the VBA portion of the forum (Office). I know I've seen plenty of posts on 64 bit pointer topics over there.
The wrapper can be walked/stepped thru. Passing invalid pointers, parameters, etc will crash. DispCallFunc is low-level and isn't friendly to mistakes. Part of the problem in your original attempt was that you needed to pass a parameter and did not. You were also assuming the string would be returned in the variant but like most property get statements, the passed parameter is overwritten with the property value. Another false assumption was that CreateObject would return the correct interface. But with many objects, they can implement more than one interface. Always QueryInterface to be sure.
Also, another key point is that the parameter pointers array are always to variants (hence: Dim vParameters() as Variant in the class). The variants contain the parameter values.
-
Jul 17th, 2017, 11:39 PM
#24
Thread Starter
Fanatic Member
Re: DispCallFunc to call COM Property/Method
Ok I seem to have successfully managed to adapt LaVolpe's code to make this work for 64Bit as well as 32Bits OSs.
I have gotten rid of the Class as well as the unnecessary CallFunction_DLL routine, Enums and other bells and whistles for simplicity's sake.
WIN 32Bits (VBA/VB6) version:
Code:
Option Explicit
Private Declare Function IIDFromString Lib "ole32" _
(ByVal lpsz As Long, ByVal lpiid As Long) As Long
Private Declare Function DispCallFunc Lib "oleaut32.dll" _
(ByVal pvInstance As Long, ByVal offsetinVft As Long, _
ByVal CallConv As Long, ByVal retTYP As Integer, ByVal paCNT As Long, _
ByRef paTypes As Integer, ByRef paValues As Long, ByRef retVAR As Variant) As Long
Private Declare Sub SetLastError Lib "kernel32.dll" (ByVal dwErrCode As Long)
Private Const CC_STDCALL As Long = 4
Sub Test()
Const IUnknownQueryInterface As Long = 0& ' IUnknown vTable offset to Query implemented interfaces
Const IUnknownRelease As Long = 8& ' IUnkownn vTable offset to decrement reference count
Const APPLICATION_INTERFACE_ID As String = "{000208D5-0000-0000-C000-000000000046}"
Const Caption_Func_Method_Offset As Long = 77 * 4 '<== Excel Caption Property_Get index=77
Dim pUNK As Long
Dim hRes As Long
Dim XL_App As Object
Dim IID_Application As Long
Dim sCaption As String
Dim aGUID(0 To 3) As Long
Set XL_App = CreateObject("Excel.Application")
pUNK = ObjPtr(XL_App)
hRes = IIDFromString(StrPtr(APPLICATION_INTERFACE_ID), VarPtr(aGUID(0)))
If hRes <> 0 Then
Exit Sub
End If
CallFunction_COM pUNK, IUnknownQueryInterface, vbLong, CC_STDCALL, VarPtr(aGUID(0)), VarPtr(IID_Application)
If IID_Application <> 0& Then
CallFunction_COM IID_Application, Caption_Func_Method_Offset, vbLong, CC_STDCALL, VarPtr(sCaption)
CallFunction_COM IID_Application, IUnknownRelease, vbLong, CC_STDCALL
End If
MsgBox sCaption
End Sub
Private Function CallFunction_COM(ByVal InterfacePointer As Long, ByVal VTableOffset As Long, _
ByVal FunctionReturnType As Long, _
ByVal CallConvention As Long, _
ParamArray FunctionParameters() As Variant) As Variant
'// minimal sanity check for these 4 parameters:
If InterfacePointer = 0& Or VTableOffset < 0& Then Exit Function
If Not (FunctionReturnType And &HFFFF0000) = 0& Then Exit Function ' can only be 4 bytes
Dim pIndex As Long, pCount As Long
Dim vParamPtr() As Long, vParamType() As Integer
Dim vRtn As Variant, vParams() As Variant
vParams() = FunctionParameters() ' copy passed parameters, if any
pCount = Abs(UBound(vParams) - LBound(vParams) + 1&)
If pCount = 0& Then ' no return value (sub vs function)
ReDim vParamPtr(0 To 0)
ReDim vParamType(0 To 0)
Else
ReDim vParamPtr(0 To pCount - 1&) ' need matching array of parameter types
ReDim vParamType(0 To pCount - 1&) ' and pointers to the parameters
For pIndex = 0& To pCount - 1&
vParamPtr(pIndex) = VarPtr(vParams(pIndex))
vParamType(pIndex) = VarType(vParams(pIndex))
Next
End If
' call the function now
pIndex = DispCallFunc(InterfacePointer, VTableOffset, CallConvention, FunctionReturnType, _
pCount, vParamType(0), vParamPtr(0), vRtn)
If pIndex = 0& Then ' 0 = S_OK
CallFunction_COM = vRtn ' return result
Else
SetLastError pIndex ' set error & return Empty
End If
End Function
2- WIN 64Bits (64Bit VBA only) version:
Code:
Option Explicit
Private Declare PtrSafe Function IIDFromString Lib "ole32" _
(ByVal lpsz As LongPtr, ByVal lpiid As LongPtr) As LongPtr
Private Declare PtrSafe Function DispCallFunc Lib "oleaut32.dll" _
(ByVal pvInstance As LongPtr, ByVal offsetinVft As LongPtr, _
ByVal CallConv As Long, ByVal retTYP As Integer, ByVal paCNT As Long, _
ByRef paTypes As Integer, ByRef paValues As LongPtr, ByRef retVAR As Variant) As Long
Private Declare PtrSafe Sub SetLastError Lib "kernel32.dll" (ByVal dwErrCode As Long)
Private Const CC_STDCALL As Long = 4
Sub Test()
Const IUnknownQueryInterface As Long = 0& ' IUnknown vTable offset to Query implemented interfaces
Const IUnknownRelease As Long = 8& ' IUnkownn vTable offset to decrement reference count
Const APPLICATION_INTERFACE_ID As String = "{000208D5-0000-0000-C000-000000000046}"
Const Caption_Func_Method_Offset As Long = 77 * 4 * 2 '<== Excel Caption Property_Get index=77
Dim pUNK As LongPtr
Dim hRes As LongPtr
Dim XL_App As Object
Dim IID_Application As Long
Dim sCaption As String
Dim aGUID(0 To 3) As Long
Set XL_App = CreateObject("Excel.Application")
pUNK = ObjPtr(XL_App)
hRes = IIDFromString(StrPtr(APPLICATION_INTERFACE_ID), VarPtr(aGUID(0)))
If hRes <> 0 Then
Exit Sub
End If
CallFunction_COM pUNK, IUnknownQueryInterface, vbLong, CC_STDCALL, VarPtr(aGUID(0)), VarPtr(IID_Application)
If IID_Application <> 0& Then
CallFunction_COM IID_Application, Caption_Func_Method_Offset, vbLong, CC_STDCALL, VarPtr(sCaption)
CallFunction_COM IID_Application, IUnknownRelease, vbLong, CC_STDCALL
End If
MsgBox sCaption
End Sub
Private Function CallFunction_COM(ByVal InterfacePointer As LongPtr, ByVal VTableOffset As Long, _
ByVal FunctionReturnType As Long, _
ByVal CallConvention As Long, _
ParamArray FunctionParameters() As Variant) As Variant
'// minimal sanity check for these 4 parameters:
If InterfacePointer = 0& Or VTableOffset < 0& Then Exit Function
If Not (FunctionReturnType And &HFFFF0000) = 0& Then Exit Function ' can only be 4 bytes
Dim pIndex As Long, pCount As Long
Dim vParamPtr() As LongPtr, vParamType() As Integer
Dim vRtn As Variant, vParams() As Variant
vParams() = FunctionParameters() ' copy passed parameters, if any
pCount = Abs(UBound(vParams) - LBound(vParams) + 1&)
If pCount = 0& Then ' no return value (sub vs function)
ReDim vParamPtr(0 To 0)
ReDim vParamType(0 To 0)
Else
ReDim vParamPtr(0 To pCount - 1&) ' need matching array of parameter types
ReDim vParamType(0 To pCount - 1&) ' and pointers to the parameters
For pIndex = 0& To pCount - 1&
vParamPtr(pIndex) = VarPtr(vParams(pIndex))
vParamType(pIndex) = VarType(vParams(pIndex))
Next
End If
' call the function now
pIndex = DispCallFunc(InterfacePointer, VTableOffset, CallConvention, FunctionReturnType, _
pCount, vParamType(0), vParamPtr(0), vRtn)
If pIndex = 0& Then ' 0 = S_OK
CallFunction_COM = vRtn ' return result
Else
SetLastError pIndex ' set error & return Empty
End If
End Function
I also tested the code with Early Binding excel (Using New keyword) and it didn't crash !
I'll need to further explore this subject but I am glad to say that this is a good start for me to gain an insight on how DispCallFunc works to call Interface functions and on COM in general so I would like to thank everyone who has kindly offered me help in this thread and particularly Mr. LaVolpe.
Regards.
-
Jul 18th, 2017, 08:15 AM
#25
Re: DispCallFunc to call COM Property/Method
You're welcome. Don't forget to mark this thread as resolved: Thread Tools menu near top of your first post.
P.S. I'm glad the class helped in your understanding of the function.
-
Jul 18th, 2017, 10:23 PM
#26
Thread Starter
Fanatic Member
Re: [RESOLVED] DispCallFunc to call COM Property/Method
Hi,
Can this technique be also applied for calling InterFace events instead of just for calling Methods/Functions like we have seen so far ?.. This is so that we could hook the events of the newly created excel application in a similar fashion.
I have noticed that the Excel TypeLib provides all VTable details for event interfaces except the corresponding VTable offsets which are missing ! ... Is there a different mechanism for accessing event interfaces ?!
Regards.
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
|