www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - [WinAPI] Problem with an interface...

reply Sascha Katzner <sorry.no spam.invalid> writes:
Hi,

I'm not sure if this is a bug in the way D handles interfaces or if I 
misunderstand something. I was trying to translate the Direct3D 10 
headers for the Windows API project: 
http://www.dsource.org/projects/bindings/browser/trunk/win32/directx

I have a Windows function (GetTechniqueByName), which returns a pointer 
to an interface (which in D results to a simple interface, because in D 
interfaces are allready reference types).

 ID3D10EffectTechnique GetTechniqueByName(LPCSTR Name);
 
 interface ID3D10EffectTechnique {
 	extern(Windows) :
 	BOOL IsValid();
 	HRESULT GetDesc(D3D10_TECHNIQUE_DESC* pDesc);
 	ID3D10EffectVariable GetAnnotationByIndex(UINT Index);
 	ID3D10EffectVariable GetAnnotationByName(LPCSTR Name);
 	ID3D10EffectPass GetPassByIndex(UINT Index);
 	ID3D10EffectPass GetPassByName(LPCSTR Name);
 	HRESULT ComputeStateBlockMask(D3D10_STATE_BLOCK_MASK* pStateBlockMask);
 }
If I try subsequently to call a function from this interface something very strange happens, the call results in the very next function from the interface declaration being called. For example if I call "technique.IsValid()" D calls GetDesc(), which results in an access violation. This behavior appears only to happen with interfaces which are NOT derived from IUnknown. I have attached a stripped down version of my source attached to this posting and a compiled version with debug info can be found at: http://sascha.katzner.de/misc/interface.exe LLAP, Sascha Katzner
Nov 22 2007
next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Sascha Katzner wrote:
 I have a Windows function (GetTechniqueByName), which returns a pointer 
 to an interface (which in D results to a simple interface, because in D 
 interfaces are allready reference types).
 
 If I try subsequently to call a function from this interface something 
 very strange happens, the call results in the very next function from 
 the interface declaration being called. For example if I call 
 "technique.IsValid()" D calls GetDesc(), which results in an access 
 violation.
 
 This behavior appears only to happen with interfaces which are NOT 
 derived from IUnknown.
Interfaces are only compatible with non-D code if they're COM interfaces (i.e. derive from IUnknown). Other interfaces are D(-compiler)-specific and thus incompatible with non-D code. This also means that "interfaces" returned from non-D code that don't derive from IUnknown can't be declared using 'interface'. However, if you know how to call their functions from C you can still use the C-like way to access them (using structs and explicitly accessing the vtable, for example). Non-COM interfaces use a D-specific vtable layout (causing you to call a different function) and calling convention (but you may've gotten "lucky" with two no-argument functions).
Nov 22 2007
parent reply Sascha Katzner <sorry.no spam.invalid> writes:
Frits van Bommel wrote:
 Interfaces are only compatible with non-D code if they're COM interfaces 
 (i.e. derive from IUnknown). Other interfaces are D(-compiler)-specific 
 and thus incompatible with non-D code. This also means that "interfaces" 
 returned from non-D code that don't derive from IUnknown can't be 
 declared using 'interface'. However, if you know how to call their 
 functions from C you can still use the C-like way to access them (using 
 structs and explicitly accessing the vtable, for example).
 
 Non-COM interfaces use a D-specific vtable layout (causing you to call a 
 different function) and calling convention (but you may've gotten 
 "lucky" with two no-argument functions).
But IUnknown itself is declared as an D interface: com.d:
 interface IUnknown
 {
     HRESULT QueryInterface(IID* riid, void** pvObject);
     ULONG AddRef();
     ULONG Release();
 } 
...or is this only some kind of false front, and the real implementation is hidden behind some compiler magic? LLAP, Sascha Katzner
Nov 22 2007
parent reply Sascha Katzner <sorry.no spam.invalid> writes:
Sascha Katzner wrote:
 But IUnknown itself is declared as an D interface:
 
 com.d:
 interface IUnknown
 {
     HRESULT QueryInterface(IID* riid, void** pvObject);
     ULONG AddRef();
     ULONG Release();
 } 
...or is this only some kind of false front, and the real implementation is hidden behind some compiler magic?
I've found something... internal\object.d:
 /**
  * Information about an interface.
  * A pointer to this appears as the first entry in the interface's vtbl[].
  */
 struct Interface
 {
     ClassInfo classinfo;	/// .classinfo for this interface (not for containing
class)
     void *[] vtbl;
     int offset;			/// offset to Interface 'this' from Object 'this'
 }
It seems this explains exactly why the next function is called (because D expects an additional first entry which Windows doesn't deliver), when using a normal D interface instead of something derived from IUnknown. What it doesn't explain is, why IUnknown interfaces doesn't have this problem?!? LLAP, Sascha Katzner
Nov 22 2007
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Sascha Katzner" <sorry.no spam.invalid> wrote in message 
news:fi4el3$1peg$1 digitalmars.com...

 It seems this explains exactly why the next function is called (because D 
 expects an additional first entry which Windows doesn't deliver), when 
 using a normal D interface instead of something derived from IUnknown. 
 What it doesn't explain is, why IUnknown interfaces doesn't have this 
 problem?!?
IUnknown is "magic", like Object. IUnknown has special significance to the compiler; it knows that anything that derives from it is a COM interface.
Nov 22 2007
prev sibling next sibling parent reply "Stewart Gordon" <smjg_1998 yahoo.com> writes:
"Sascha Katzner" <sorry.no spam.invalid> wrote in message 
news:fi3t6f$1009$1 digitalmars.com...
 Hi,

 I'm not sure if this is a bug in the way D handles interfaces or if I
 misunderstand something. I was trying to translate the Direct3D 10
 headers for the Windows API project:
 http://www.dsource.org/projects/bindings/browser/trunk/win32/directx

 I have a Windows function (GetTechniqueByName), which returns a pointer
 to an interface (which in D results to a simple interface, because in D
 interfaces are allready reference types).

 ID3D10EffectTechnique GetTechniqueByName(LPCSTR Name);

 interface ID3D10EffectTechnique {
 extern(Windows) :
<snip> Most of the interface definitions don't have extern(Windows) in them. Are you sure it's right to have it here? Stewart. -- My e-mail address is valid but not my primary mailbox. Please keep replies on the 'group where everybody may benefit.
Nov 22 2007
parent Sascha Katzner <sorry.no spam.invalid> writes:
Stewart Gordon wrote:
 Most of the interface definitions don't have extern(Windows) in them.  
 Are you sure it's right to have it here?
I think extern(Windows) is correct here. BaseTyps.h:
 #define STDMETHODCALLTYPE       __stdcall
 #define STDMETHOD(method)        virtual COM_DECLSPEC_NOTHROW HRESULT
STDMETHODCALLTYPE method
 #define STDMETHOD_(type,method)  virtual COM_DECLSPEC_NOTHROW type
STDMETHODCALLTYPE method
D3D10effect.h:
 DECLARE_INTERFACE(ID3D10EffectTechnique)
 {
     STDMETHOD_(BOOL, IsValid)(THIS) PURE;
     STDMETHOD(GetDesc)(THIS_ D3D10_TECHNIQUE_DESC *pDesc) PURE;
     STDMETHOD_(ID3D10EffectVariable*, GetAnnotationByIndex)(THIS_ UINT Index)
PURE;
     STDMETHOD_(ID3D10EffectVariable*, GetAnnotationByName)(THIS_ LPCSTR Name)
PURE;
     STDMETHOD_(ID3D10EffectPass*, GetPassByIndex)(THIS_ UINT Index) PURE;
     STDMETHOD_(ID3D10EffectPass*, GetPassByName)(THIS_ LPCSTR Name) PURE;
     STDMETHOD(ComputeStateBlockMask)(THIS_ D3D10_STATE_BLOCK_MASK
*pStateBlockMask) PURE;
 };
LLAP, Sascha Katzner
Nov 22 2007
prev sibling parent Sascha Katzner <sorry.no spam.invalid> writes:
Since it seems that a D interface is not usable in this case, I've 
solved this problem like Frits suggested with a struct as vtbl:

 struct ID3D10EffectTechniqueVtbl {
 	extern(Windows) :
 	BOOL function() IsValid;
 	HRESULT function(D3D10_TECHNIQUE_DESC* pDesc) GetDesc;
 	ID3D10EffectVariable function(UINT Index) GetAnnotationByIndex;
 	ID3D10EffectVariable function(LPCSTR Name) GetAnnotationByName;
 	ID3D10EffectPass function(UINT Index) GetPassByIndex;
 	ID3D10EffectPass function(LPCSTR Name) GetPassByName;
 	HRESULT function(D3D10_STATE_BLOCK_MASK* pStateBlockMask)
ComputeStateBlockMask;
 }
 alias ID3D10EffectTechniqueVtbl* ID3D10EffectTechnique;
It is a little bit inconvenient, because now I have two different kinds of "interfaces", but it works. ;-) Thanks all, Sascha Katzner
Nov 22 2007