Need help in debugging a stack corruption

Stefan Dösinger stefandoesinger at gmx.at
Sat Jan 7 05:02:27 CST 2006


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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : http://www.winehq.org/pipermail/wine-devel/attachments/20060107/da95d386/attachment.pgp


More information about the wine-devel mailing list