Vtable different between C++ and Freebasic? [SOLVED]

General FreeBASIC programming questions.
stephanbrunker
Posts: 62
Joined: Nov 02, 2013 14:57

Vtable different between C++ and Freebasic? [SOLVED]

Postby stephanbrunker » Mar 11, 2014 21:42

Hello,

I'm trying to implement some Windows Interfaces in Freebasic, using virtual methods in the same manner as windows does:

Code: Select all

Type JUnknown EXTENDS OBJECT   'IUnknown Interface
       
      Declare Constructor()
      Declare Destructor()
      
      'Methods:
      'IUnknown Interface:   
      Declare Virtual Function QueryInterface (ByVal iid As REFIID, ByVal ppvObject As Any Ptr Ptr) As HRESULT
      Declare Virtual Function AddRef () As ULong
      Declare Virtual Function Release () As ULong
      
      'member variables:
      As Long       m_lRefCount             

End Type


and the second one:

Code: Select all

Type JDataObject EXTENDS JUnknown

   Declare Constructor(fmtetc () As FORMATETC , stgmed () As STGMEDIUM)
   Declare Destructor()

   'Methods:
   'IUnknown Interface:      
   Declare Virtual Function QueryInterface ( ByVal iid As IID Ptr, ByVal ppvObject As PVOID Ptr) As HRESULT

   'IDataObject Interface:
   Declare Virtual Function GetData ( ByVal pfmtetc As FORMATETC Ptr, ByVal pstgmed As STGMEDIUM Ptr) As HRESULT
   Declare Virtual Function GetDataHere ( ByVal pfmtetc As FORMATETC Ptr, ByVal pstgmed As STGMEDIUM Ptr) As HRESULT
   Declare Virtual Function QueryGetData (ByVal pfmtetc As FORMATETC Ptr) As HRESULT
   Declare Virtual Function GetCanonicalFormatEtc ( ByVal pfmtetc As FORMATETC Ptr, ByVal pfmtetc2 As FORMATETC Ptr) As HRESULT
   Declare Virtual Function SetData ( ByVal pfmtetc As FORMATETC Ptr, ByVal pstgmed As STGMEDIUM Ptr, ByVal As BOOL) As HRESULT
   Declare Virtual Function EnumFormatEtc ( ByVal dwDirection As DWORD, ByVal ppEnumFmtetc As JEnumFORMATETC Ptr Ptr) As HRESULT
   Declare Virtual Function DAdvise ( ByVal pfmtetc As FORMATETC Ptr, ByVal advf As DWORD, ByVal pAdvSink As IAdviseSink Ptr, ByVal pDwConnection As PDWORD) As HRESULT
   Declare Virtual Function DUnadvise ( ByVal  dwConnection As DWORD) As HRESULT
   Declare Virtual Function EnumDAdvise ( ByVal ppEnumAdvise As IEnumSTATDATA Ptr Ptr) As HRESULT
   
   'member variables:
   As Integer m_nNumFormats   
   As STGMEDIUM Ptr m_pstgmed = Any
   As FORMATETC Ptr m_pfmtetc = Any
   
End Type


but unfortunately, when I send the Pointer to my IDataobject to the DoDragDrop function, it calls correct the IUnknown Members, but as far as I could determinate, Windows then calls GetGata, but this seems to be wrong, because the parameters don't make sense. The only explanation I've at the moment is that the vtable is different in C+ and Freebasic, so that the pointers to the functions are offset and another function should be called. Couriously, if I get a pointer to a Windows-generated dataobject and call GetData with the same TYPE, I get the correct answer.

I've a fallback in form of a UDT without Virtual Functions and derivated Types, it contains static functions and put the pointers to them into the first member of the udt (the IdataObjectVtbl type in the Windows header files):

Code: Select all

Type CDataObject 'implements Custom IDataObject Interface

   'implements IDataObject@CDataObject
   As IDataObject m_DataObject = Any

   Declare Constructor(fmtetc () As FORMATETC , stgmed () As STGMEDIUM)
   Declare Destructor()
   
   'Methods:
   'IUnknown Interface:      
   Declare Static Function QueryInterface (ByVal pData as IDataObject ptr, byval iid As IID ptr, byval ppvObject As PVOID ptr) as HRESULT
   Declare Static Function AddRef (byval pData As IDataObject ptr) as ULONG
   Declare Static Function Release (ByVal pData as IDataObject ptr) as ULONG
   
   'IDataObject Inteface:
   Declare Static Function GetData (byval pData As IDataObject ptr, byval pfmtetc As FORMATETC ptr, byval pstgmed As STGMEDIUM ptr) as HRESULT
   Declare Static Function GetDataHere (ByVal pData as IDataObject ptr, byval pfmtetc As FORMATETC ptr, ByVal pstgmed as STGMEDIUM ptr) as HRESULT
   Declare Static Function QueryGetData (ByVal pData as IDataObject ptr, ByVal pfmtetc as FORMATETC ptr) as HRESULT
   Declare Static Function GetCanonicalFormatEtc (ByVal pData as IDataObject ptr, ByVal pfmtetc as FORMATETC ptr, byval pfmtetc2 As FORMATETC ptr) as HRESULT
   Declare Static Function SetData (ByVal pData as IDataObject ptr, ByVal pfmtetc as FORMATETC ptr, byval pstgmed As STGMEDIUM ptr, byval as BOOL) as HRESULT
   Declare Static Function EnumFormatEtc (byval pData As IDataObject ptr, ByVal dwDirection as DWORD, ByVal ppEnumFmtetc As IEnumFORMATETC ptr ptr) as HRESULT
   Declare Static Function DAdvise (byval pData As IDataObject ptr, ByVal pfmtetc as FORMATETC ptr, byval advf As DWORD, byval pAdvSink As IAdviseSink ptr, ByVal pDwConnection as PDWORD) as HRESULT
   Declare Static Function DUnadvise (byval pData As IDataObject ptr, byval  dwConnection As DWORD) as HRESULT
   Declare Static Function EnumDAdvise (byval pData As IDataObject ptr, ByVal ppEnumAdvise as IEnumSTATDATA ptr ptr) as HRESULT
   
   'helper Functions:
   Declare Static Function LookupFormatEtc(ByVal pData As IDataObject Ptr, ByVal pfmtetc As FORMATETC ptr) As Integer
   Declare Static Function dupmem(ByVal hMem As HGLOBAL) As HGLOBAL
   
   'member variables:
   As Integer m_lRefCount
   As Integer m_nNumFormats   
   As STGMEDIUM Ptr m_pstgmed = Any
   As FORMATETC Ptr m_pfmtetc = Any
   
End Type

Constructor CDataObject (fmtetc() As FORMATETC, stgmed () As STGMEDIUM)   
   Static As IDataObjectVtbl vtbl = _
      ( _
      @CDataObject.QueryInterface, _
      @CDataObject.AddRef, _
      @CDataObject.Release, _
      @CDataObject.GetData, _
      @CDataObject.GetDataHere, _
      @CDataObject.QueryGetData, _
      @CDataObject.GetCanonicalFormatEtc, _
      @CDataObject.SetData, _
      @CDataObject.EnumFormatEtc, _
      @CDataObject.DAdvise, _
      @CDataObject.DUnadvise, _
      @CDataObject.EnumDAdvise _
      )
   m_DataObject.lpVtbl = @vtbl   

   'initalize member variables   
   m_lRefCount  = 1
   
   'get FORMATETC/STGMEDIUM data
   Dim count As ULong = UBound(fmtetc)+1   
   m_nNumFormats = count
   m_pstgmed = New STGMEDIUM[count]
   m_pfmtetc = New FORMATETC[count]
   Dim i As Integer
   For i = 0 To count - 1
      m_pstgmed[i] = stgmed(i)
      m_pfmtetc[i] = fmtetc(i)
   Next i
   Print "CDataObject::Constructor [";m_nNumFormats; " Formats]"   
End Constructor


But this is not really better. It works, but there are a lot of wrong calls to the first three members instead, IDropSource.QueryInterface gets called multiple times but it's obvious not the interface the caller expects. That works obviously better in the first version. Then IDataObject::EnumFormatEtc get called, but this is also dubious because normally IDropTarget::DragEnter should be the next Method to be called. Has anyone an answer what's wrong?
Last edited by stephanbrunker on Mar 12, 2014 17:19, edited 1 time in total.
dkl
Site Admin
Posts: 3211
Joined: Jul 28, 2005 14:45
Location: Germany

Re: Vtable different between C++ and Freebasic?

Postby dkl » Mar 12, 2014 8:00

It looks correct to me:

Code: Select all

__ZTVN8JUNKNOWNE:
.long 0 (null pointer)
.int __ZTSN8JUNKNOWNE (JUnknown RTTI table)
.int __ZN8JUNKNOWN14QUERYINTERFACEEP4GUIDPPv@12 (JUnknown.QueryInterface)
.int __ZN8JUNKNOWN6ADDREFEv@4 (JUnknown.AddRef)
.int __ZN8JUNKNOWN7RELEASEEv@4 (JUnknown.Release)

...

__ZTVN11JDATAOBJECTE:
.long 0 (null pointer)
.int __ZTSN11JDATAOBJECTE (JDataObject RTTI table)
.int __ZN11JDATAOBJECT14QUERYINTERFACEEP4GUIDPPv@12 (JDataObject.QueryInterface)
.int __ZN8JUNKNOWN6ADDREFEv@4 (JUnknown.AddRef)
.int __ZN8JUNKNOWN7RELEASEEv@4 (JUnknown.Release)
.int __ZN11JDATAOBJECT7GETDATAEP9FORMATETCP9STGMEDIUM@12 (JDataObject.GetData)
.int __ZN11JDATAOBJECT11GETDATAHEREEP9FORMATETCP9STGMEDIUM@12 (JDataObject.GetDataHere)
.int __ZN11JDATAOBJECT12QUERYGETDATAEP9FORMATETC@8 (JDataObject.QueryGetData)
.int __ZN11JDATAOBJECT21GETCANONICALFORMATETCEP9FORMATETCS1_@12 (JDataObject.GetCanonicalFormatEtc)
.int __ZN11JDATAOBJECT7SETDATAEP9FORMATETCP9STGMEDIUMi@16 (JDataObject.SetData)
.int __ZN11JDATAOBJECT13ENUMFORMATETCEjPPv@12 (JDataObject.EnumFormatEtc)
.int __ZN11JDATAOBJECT7DADVISEEP9FORMATETCjP11IADVISESINKPj@20 (JDataObject.DAdvise)
.int __ZN11JDATAOBJECT9DUNADVISEEj@8 (JDataObject.DUnadvise)
.int __ZN11JDATAOBJECT11ENUMDADVISEEPP13IENUMSTATDATA@8 (JDataObject.EnumDAdvise)


Since the object's vptr will be initialized to point to the 3rd element (@vtable + 2) it looks to me like the layout matches the IDataObject vtable declaration in the MinGW-w64 headers. The methods are in the correct order, they use stdcall, the parameters/results look correct, there are no other unrelated entries in between.
stephanbrunker
Posts: 62
Joined: Nov 02, 2013 14:57

Re: Vtable different between C++ and Freebasic?

Postby stephanbrunker » Mar 12, 2014 9:08

Thank you for looking - the error has to be somewhere else, because the IDropTarget Interface works correctly and get it's methods called in the right order. My next guess is a maybe incorrect handling of the QueryInterface calls. Before DoDragDrop calls one of the interfaces' methods, it confirms with QueryInterface and maybe the ppvobject return value isn't correct.

Edit:
Small steps: If I get a Pointer to an IDataObject Interface, the type works correct and i can call every method. But other way round, afert calling JDataobject:QueryInterface, Windows calls next JDataobject:GetData but meant also QueryInterface, because the Argument is a REFIID. The Question is why, because I answer the previous call to QueryInterface with @this:

Code: Select all

Function JDataObject.QueryInterface ( ByVal iid As IID Ptr,ByVal ppvObject As Any Ptr Ptr) As HRESULT
   Print "IDataObject::QueryInterface"
   'if it's the right format return a pointer to the interface
   If IsEqualIID ( iid, @IID_IUnknown) Or IsEqualIID( iid, @IID_IDataObject) Then
      AddRef()
      *ppvObject = @This
      Print "S_OK"
      Return S_OK
   Else
      *ppvObject = NULL
      Print "E_NOINTERFACE"
      Return E_NOINTERFACE
   End If
   
End Function


or is simply the row *ppvObject = @This wrong? The MSDN says:

ppvObject [out]
The address of a pointer variable that receives the interface pointer requested in the riid parameter. Upon successful return, *ppvObject contains the requested interface pointer to the object. If the object does not support the interface, *ppvObject is set to NULL.
fxm
Posts: 9701
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Vtable different between C++ and Freebasic?

Postby fxm » Mar 12, 2014 13:20

Just a small remark from your different posts (I don't know if there is an impact on the bug):
JDataObject.QueryInterface(...) does not override JUnknown.QueryInterface(...) (no polymorphism despite of virtual declaration) because the parameter signature is different between the two declarations!
dkl
Site Admin
Posts: 3211
Joined: Jul 28, 2005 14:45
Location: Germany

Re: Vtable different between C++ and Freebasic?

Postby dkl » Mar 12, 2014 14:13

I think after expanding the type aliases (typedefs) they're the same, that's why fbc inserts the overrides into the vtable as expected.

That QueryInterface() looks good to me too (though I have no experience with COM).
fxm
Posts: 9701
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Vtable different between C++ and Freebasic?

Postby fxm » Mar 12, 2014 14:55

Yes.

But perhaps the descriptor should it be virtual with interfaces?
(I apologize in proposing ideas can be ridiculous!)
stephanbrunker
Posts: 62
Joined: Nov 02, 2013 14:57

Re: Vtable different between C++ and Freebasic?

Postby stephanbrunker » Mar 12, 2014 17:18

Thank you all, but I've got the problem fixed! (and it was a hard one to find!):

The problem was the IsEqualIID - Function in QueryInterface. Following the description, it should be returning TRUE or FALSE. But that isn't correct. After printing every IID's in the communication and comparing manually and also printing the result of that function, I found out that the function returns without any obvious reason -2 or 0 for false and -1 for true. Because of this behaviour, Freebasic didn't recogize any value as true (def'ed to 1) in

Code: Select all

   If (IsEqualIID ( iid, @IID_IUnknown) = TRUE) Or (IsEqualIID( iid, @IID_IDropSource) = TRUE) Then


and every -2 return as false true value in

Code: Select all

   If IsEqualIID ( iid, @IID_IUnknown) Or IsEqualIID( iid, @IID_IDropSource) Then


it only gives the correct return value when written in actual numbers:

Code: Select all

   If (IsEqualIID ( iid, @IID_IUnknown) = -1) Or (IsEqualIID( iid, @IID_IDropSource) = -1) Then


I fairly don't have a clue what DoDragDrop wants to call when every QueryInterface Call gets answered with S_OK in one case and I understand that the operation is aborted when every call gets negated, but it was as simple as that. (And mostly I'm angering myself because I had a working version with memcmp instead of IsEqualIID in the previous, non-inherited version of the interfaces and it was one more part which I changed when rewriting.

But I appreciate the help and if I don't bump against unsolvable problems, i'm going to post and describe the code in a Tutorial at http://www.freebasic-portal.de , also including a version with IStream and CF_FILEDESCRIPTOR for virtual Files.
dkl
Site Admin
Posts: 3211
Joined: Jul 28, 2005 14:45
Location: Germany

Re: Vtable different between C++ and Freebasic? [SOLVED]

Postby dkl » Mar 12, 2014 18:43

Hmm, that makes sense. I think FB's IsEqualIID()/IsEqualGUID() declaration is buggy:

inc/win/objbase.bi

Code: Select all

'' old, wrong:
#define IsEqualGUID(rguid1, rguid2) (not memcmp(rguid1, rguid2, sizeof(GUID)))
'' it should be:
#define IsEqualGUID(rguid1, rguid2) (-(memcmp(rguid1, rguid2, sizeof(GUID)) = 0))
TJF
Posts: 3596
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

Re: Vtable different between C++ and Freebasic? [SOLVED]

Postby TJF » Mar 12, 2014 20:38

Better use

Code: Select all

#DEFINE IsEqualGUID(rguid1, rguid2) (IIF(memcmp(rguid1, rguid2, sizeof(GUID)), 0, 1))
aloberoger
Posts: 482
Joined: Jan 13, 2009 19:23

Re: Vtable different between C++ and Freebasic? [SOLVED]

Postby aloberoger » Mar 13, 2014 8:33

JEnumFORMATETC definition is Missing
In the other hand you try to mix virtual methods with vtbl C++ don't do that, C++ use only Virtual methods
take a look on my works INSIDE ACTIVEX WITH FREEBASIC

Return to “General”

Who is online

Users browsing this forum: No registered users and 3 guests