Hi,
Best would to see the actual code for that as I do not really understand what you did by reading your description of it.
I only have to implement D3D Textures, IDirect3DVertexBuffer and IDirect3DExecuteBuffer, then my new patch will be more or less complete and I'll post it here. Shouldn't take too long, depending on the work I have to do for University ;)
But I still find what you wrote suspicious: if you have 4 VTables you should NEVER cast functions even if they have the same signature - casts are only useful if multiple object versions share the same VTable. Basically (from what I remember :-) ), the pointer to the VTable is stored at the address returned to the application as the COM object. Wine then use a fixed offset to find it's private data from the COM object (basically, the offset between the start of Wine's data to the VTable it returned to the application). Of course, if you have 4 VTables, these offsets are different => you cannot find the address of Wine's internal data without knowing exactly which object was given as an argument to the function.
That's basically what the original version does with the macros in ddcomimpl. When I looked at those I didn't really understand their functionality and what they were good for, so I did it a little different.
That's what it basically looks like( IDirectDraw for example):
My IDirectDrawImpl structure :
struct IDirectDrawImpl { IDirectDraw7Vtbl *lpVtbl; ULONG ref;
/* Other members follow here */ }
Most implementation functions are DD7: HRESULT WINAPI IDirectDrawImpl_SetCooperativeLevel(IDirectDraw7 *iface, HWND hWnd, DWORD dwFlags); HRESULT WINAPI IDirectDrawImpl_SetDisplayMode(IDirectDraw7 *iface, DWORD dwWidth, DWORD dwHeight, DWORD dwBPP, DWORD dwRefreshRate, DWORD dwFlags);
I have 4 versions of the Vtables:
IDirectDraw7Vtbl IDirectDraw7_Vtbl = { /* IUnknown */ ... /* IDirectDraw7 */ ... IDirectDrawImpl_SetCooperativeLevel, IDirectDrawImpl_SetDisplayMode, ... }
IDirectDraw4Vtbl IDirectDraw4_Vtbl = { /* IUnknown */ ... /* IDirectDraw4 */ ... ( void * ) IDirectDrawImpl_SetCooperativeLevel, ( void * ) IDirectDrawImpl_SetDisplayMode, ... }
The arguments for SetCooperativeLevel and SetDisplayMode are the same for DD7 and DD4 ( IDirectDrawX *, HWND, DWORD), (IDirectDrawX *, DWORD, DWORD, DWORD, DWORD, DWORD). The only difference is the IDirectDrawX pointer, so I have to use a cast to supress the warning in older Vtables( The (void *) cast is equal to the XCAST makro in the original implementation).
A problem arises then the arguments are different in varios versions: For example IDirectDraw::SetDisplayMode: it only takes a Pointer and 3 DWORDs. Do I created another function:
HRESULT WINAPI IDirectDrawImpl1_SetDisplayMode(IDirectDraw *iface, DWORD dwWidth, DWORD dwHeight, DWORD dwBPP);
And for the DD1 Vtable I use
IDirectDrawVtbl IDirectDraw1_Vtbl = { /* IUnknown */ ... /* IDirectDraw4 */ ... ( void * ) IDirectDrawImpl_SetCooperativeLevel, IDirectDrawImpl1_SetDisplayMode, ... }
Such functions eighter contain a independent implementation, like in SetDisplayMode(which is only a one-liner call to WineD3D) or they call the V7 implementation(Like in IDirect3DDevcie). I can't use the VTable for this, because there's no DD7 Vtable assigned, so I call the I<Iface>Impl_<Method> function directly(This happens a few times in IDirect3DDevice).
In the implementation functions I can get the Implementation structure easily:
IDirectDrawImpl *This = (IDirectDrawImpl *) iface;
There's no difference in the address of the VTable and the Implementation structure.
When creating the Interface, I allocate a I<Interface>Impl structure, and I assign the VTable of the requested version:
if(DD7) object->lpVtbl = &IDirectDraw7_Vtbl; else if(DD4) (IDirectDraw4Vtbl *) object->lpVtbl = &IDirectDraw4_Vtbl; else if(DD2) (IDirectDraw2Vtbl *) object->lpVtbl = &IDirectDraw2_Vtbl; else if(DD1) (IDirectDrawVtbl *) object->lpVtbl = &IDirectDraw1_Vtbl;
If any implementation funtion really needs to know the version of the interface, it can compare the VTable addresses:
if(This->lpVtbl == &IDirectDraw7_Vtbl) { DD7 } else if( (IDirectDraw4Vtbl *) This->lpVtbl == &IDirectDraw4_Vtbl) { DD4 } ....
So I do not need a Version member in the Implementation structures ;)
This works for all games I tried so far, except for HL 1.1.1.0, so I suspect the problem to be somewhere else. HL 1.1.1.1 (the Steam version) works, except that it crashes in native shdocvw when entering the game(This happens with the original ddraw version too). OpenGL mode still works fine. If there are problems I don't know yet, they'll arise in IDirect3DDevice, as it has heavily different VTables.
What are the advantages of this? * No need for the Thunk_I<Interface>_<Method> functions, except for IDirectDraw::SetDisplayMode. The app calls directly into the methods. * No need for the ICOM_THIS_FROM, COM_INTERFACE_CAST, ... Makros. * No difference between the Implementation address and the address passed to the app. This makes traces easier to read IMO.
The problems? * The (void *) cast makes it hard to find incorrectly assigned VTable members. Maybe that's the problem with HL 1.1.1.0 * Implementation functions can't use the VTables * It's different from other dlls that use multiple interface versions. (Are there any? I found only ddraw)
I think I can send a new patch in the next 2 weeks, so you can look at the whole thing.
Stefan