Am Freitag, den 27.06.2008, 09:13 -0400 schrieb Chris Ahrendt:
The problem is that currently, if the application requests a IDirectDrawSurface4 what it gets is a IDirectDrawSurface7. Which is (on the first look) not a issue, as the vtables are compatible (same functions with same signature at same offsets, except for IDirectDrawSurface7 having added some functions at the end), but on the second look, the IDirectDrawSurface7 vtable contains the pointer to the strict AddAttachedSurface, whereas the vtable for IDirectDraw4 must contain a pointer to a relaxed AddAttachedSurface. This we need two different vtables (You can query an IDirectDraw7 and an IDirectDraw4 surface on the same object, so a flag in the object does not do, probably[1]) to get the different behaviours. Two different vtables for the same object inherently is thunking.
I wonder if there is an easy way to do an API abstraction of some sort... not with a flag perse but like you can in C++ with the different instanciations depending upon the parms passed.
If I understand you correctly, you are thinking of C++ templates or overloaded functions here. They are not comparable, as both are compile-time polymorphism, while here run-time polymorphism is needed. If I write "cout << 1", the compiler uses std::ostream& operator <<(std::ostream&, int) and if I write "cout << 1.2", the compiler uses std::ostream& operator <<(std::ostream&, double) These are two different functions, that just happen to have the same name in the source code. In object code, the mangled names are used, which are different.
Templates also go along this line: The compiler instantiates the template with concrete types (so we have std::vector<int> and std::vector<double>), that just both happen to be instantiated from the same template, but have nothing in common from the viewpoint of the linker or runtime-environment.
Run-time polymorphism in C++ (and in COM, DirectDraw is a COM interface) is implemented via vtables. If you want IDirectDrawSurface7::AddAttachedSurface to call a different function than IDirectDirectDrawSurface4::AddAttachedSurface, then the function pointer at address 0x0c in the vtable for IDirectDrawSurface4 must be different from the function pointer at address 0x0c in the vtable for IDirectDrawSurface7. So it is obvious, that two different vtables are needed.
A COM interface pointer points directly to the vtable pointer of the interface you requested. So for the even same surface, the IDirectDraw4 interface pointer must be different from the IDirectDraw7 interface pointer, as the address stored at the destination of the interface pointer is either the IDirectDraw4 or the IDirectDraw7 vtable address. So we need two different interface pointers into the same object. As the core functions need a pointer that points to one defined location in the object, the methods called via the second vtable have to adjust the interface pointer they received into the original interface pointer, and pass it on to the core implementation. And this is where I discovered what a thunk it: Exactly a function that adjusts the this pointer before calling the main implementation; these thunks are unavoidable if functions from one vtable are forwarded to functions belonging to another vtable implemented by the same object.
Then instead of thunking, which is inherently buggy
I don't agree with this statement.
and tends to be slow, you use the v7 structure and return it without the v7 specific items at the end.
Tables get not returned. Table pointers are included in the COM objects and pointed to by interface pointers. Using the v7 table for v4 is what wine currently does, and causes the problem discussed in this thread.
That way you store one table instead of 2. (again sorry if this is fuzzy I am working on no sleep here).
Doesn't work. The fourth entry in that table *either* points to the strict *or* to the relaxed version of AddAttachedSurface. I might get around to send a patch on sunday.
Regards, Michael Karcher