Huge DLL class problem/confusion


DevX Home    Today's Headlines   Articles Archive   Tip Bank   Forums   

Results 1 to 10 of 10

Thread: Huge DLL class problem/confusion

  1. #1
    Join Date
    Feb 2006
    Location
    Sweden
    Posts
    6

    Huge DLL class problem/confusion

    After tons of testing I have found the root of my problem. When multiple methods in a class interface have the same name (but different argument lists) their order gets reversed on some compilers, so if you try to call the one with no arguments (the first) you actually call the one with 2 arguments (the last) and so on. This is mighty odd, what causes this and what can I do to solve it? (apart from using unique names which I DON'T want :) ).

    The client below gives 1 2 3 in VC++2003 but 3 2 1 in DevCPP.

    The DLL is ALWAYS compiled in VC++2003 (the client in both VC and DevCPP).

    Dll:
    Code:
    #include <windows.h>
    
    interface I
    {
    	virtual INT __stdcall Get() = 0;
    	virtual INT __stdcall Get(INT X) = 0;
    	virtual INT __stdcall Get(INT X, INT Y) = 0;
    };
    
    class C : public I
    {
    	public:
    		C() { }
    		~C() { }
    
    		virtual INT __stdcall Get() { return 1; };
    		virtual INT __stdcall Get(INT X) { return 2; };
    		virtual INT __stdcall Get(INT X, INT Y) { return 3; };
    };
    
    I* c = NULL;
    
    extern "C" __declspec(dllexport) I* GetC()
    {
    	return c;
    }
    
    BOOL WINAPI DllMain(HINSTANCE Dll, DWORD Reason, LPVOID Reserved)
    {
    	switch(Reason)
    	{
    		case DLL_PROCESS_ATTACH:
    		{
    			c = new C();
    			break;
    		}
    
    		case DLL_PROCESS_DETACH:
    		{
    			delete c;
    			break;
    		}
    	}
    
    	return TRUE;
    }
    Client:
    Code:
    #include <windows.h>
    #include <sstream>
    
    interface I
    {
    	virtual INT __stdcall Get() = 0;
    	virtual INT __stdcall Get(INT X) = 0;
    	virtual INT __stdcall Get(INT X, INT Y) = 0;
    };
    
    typedef I* (*GET_C)();
    
    INT WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, INT)
    {
    	I* c;
    	GET_C GetC;
    	std::stringstream Stream;
    
    	HINSTANCE Dll = LoadLibrary("DllMultiCompilerTest.dll");
    	if(Dll == NULL)
    	{
    		MessageBox(NULL, "No dll!", "Message", MB_OK);
    		return 0;
    	}
    
    	GetC = reinterpret_cast<GET_C>(GetProcAddress(Dll, "GetC"));
    	if(GetC == NULL)
    	{
    		FreeLibrary(Dll);
    		MessageBox(NULL, "No func!", "Message", MB_OK);
    		return 0;
    	}
    
    	c = GetC();
    	if(c == NULL)
    	{
    		FreeLibrary(Dll);
    		MessageBox(NULL, "No object!", "Message", MB_OK);
    		return 0;
    	}
    
    	Stream << "Get (should be 1 2 3):\n" << c->Get() << " " << c->Get(0) << " " << c->Get(0, 0);
    	MessageBox(NULL, Stream.str().c_str(), "Message", MB_OK);
    
    	FreeLibrary(Dll);
    
    	return 0;
    }
    Last edited by Magos; 02-21-2006 at 05:14 PM.

  2. #2
    Join Date
    Nov 2003
    Posts
    4,118
    Why do you think the member functions' oder is reversed? Just by looking at the output of the cout statement? The order may well be right, but cout has its own stack for buffering output. When the stack is filled up, cout pops its elements. This is of course done in the reverse order because that's how stacks work. A better way to check your code is by adding an endl; after each member function call.
    Danny Kalev

  3. #3
    Join Date
    Feb 2006
    Location
    Sweden
    Posts
    6
    Huh? I'm not even using cout, and even if I were it *should* still display stuff in the order it's given (or the implementation is poor).
    This is just a small test program to simulate the error, in my real prog I use no streams whatsoever (the order-error still remains of course).

    EDIT: By order I don't mean in which order Get is called, but when I call Get with no arguments it returns what Get with 2 arguments should return and vice-versa!

    EDIT2: Forgot to mention, the DLL is ALWAYS compiled in VC++2003 (the client in both VC and DevCPP).
    Last edited by Magos; 02-21-2006 at 05:15 PM.

  4. #4
    Join Date
    Nov 2003
    Posts
    4,118
    "By order I don't mean in which order Get is called, but when I call Get with no arguments it returns what Get with 2 arguments should return and vice-versa!" And how do you know that? Just by looking at the order by which the return values are stacked? That's not a reliable criterion. You should do something else like logging each call, and having the callee log itself, say in a data file. Better yet, call only one function and see what the result is.
    If indeed the wrong function gets called, it's probably a result of a mismatch between the binaries and the source files. Rebuild your entire project and the DLL itself. Also, make sure that these functions aren't inlined (define them oustide the class body) and finally, make sure that all the calling conventions are identical.
    Danny Kalev

  5. #5
    Join Date
    Feb 2006
    Location
    Sweden
    Posts
    6
    Of course there's a mismatch, VC and DevCPP generates different class structures, I want to know how to make them compatible. It is possible, DirectX and COM does this! I want to know how!

    And NO this is NOT a problem with the output, the WRONG method IS called!!! I have no idea what you're trying to say about stacks, if I do this:
    Code:
    func1();
    func2();
    func1 should ALWAYS, ALWAYS,ALWAYS be called first no matter what stacking schemes the compiler uses. And for the record, YES I have called just a single method which gives incorrect value.

    I get a feeling you're missing my point, re-read my posts.

  6. #6
    Join Date
    Nov 2003
    Posts
    4,118
    If the DLL is created by VS, it could be a pointer size problem as well. In VS2005 (and probably 2003, check your compiler's documentation), the size of function pointer is 8 bytes. DevCPP probably uses 32 bits for this. It could also affect the size of the vptr inside the object: the DLL uses 8 bytes whereas the client code uses 4 bytes etc. In short make sure that both compilers use the same alignmnet and the same pointer sizes. As I said, you need to make sure that the functions aren't inlined either, and that the DLL is freshly compiled. It is an offset calculation problem of some sort.
    BTW, don't be so sure about the ALWAYS ALWAYS ALWAYS part. There are cases in which the compiler is allowed to re-arrange calls in a different order, e.g., when the function calls are within an argument list of another function call for instance. And finally, if you want people to answer your questions, please stick to a courteous language.
    Last edited by Danny; 02-22-2006 at 08:11 PM.
    Danny Kalev

  7. #7
    Join Date
    Feb 2006
    Location
    Sweden
    Posts
    6
    Well, if there was a mismatch in pointer sizes there would be no erronous cross-calls (a calls b and b calls a),there would be a calls a, b calls c, c calls e etc...

    The trouble is the v-table but I have currently no idea how to solve it. I checked out the __declspec(novtable) keywords but it didn't help much. DX and COM can be used in multiple compilers so it has to be done in some way.

    Sorry bout the language in that last post, I was tired and no one in any of the forums I've posted this in can give any real help so I popped a bit when I thought people assumed I was some newbie with a simple problem. Anyway, thanks for your time...

  8. #8
    Join Date
    Nov 2003
    Posts
    4,118
    It isn't a simple problem at all. It appears as if the binary layout of the object is different in both compilers. You can't predict how virtual functions' pointers are ordered in the vtable: they are completely compiler specific so whereas VS assumes get1, get2, etc., another compiler might reorder them in the reverse order. Another poential problem: the location of the vptr itself. It could either be at offset 0, i.e. the first data member, or it could be the last. Again, different compilers employ different layouts. As for COM and DirectX: they aren't quite the same because the compiler knows to generate additional metadata for each class.
    Notice that your interface class doesn't declare a destructor. It should, and that destrcutor should be virtual as well.

    As a sanity check, try to obtain the size of each class on each platform and compare the results. If they sizes don't match, this could indicate that the vptr's size isn't identical. Another potential problem is the onerous byte ordering problem. Do the server as the client run on Windows?
    Danny Kalev

  9. #9
    Join Date
    Feb 2006
    Location
    Sweden
    Posts
    6
    I was reading this article and at the very bottom I get my very long awaited (and feared) answer, method overloading is indeed tabu in binary compatibility! Oh well...

    Hm, I wonder if it's possible to fake overloading on the client side using say macros? With all different sized argument lists it might work, otherwise I'll just have to accept using different names...

  10. #10
    Join Date
    Nov 2003
    Posts
    4,118
    So it is a binary layout problem. Mystery solved.
    I don't think you need overloading in the first place. Simply use a single function with default values for all arguments. This way the function can tell how many real arguments it was given, and delegate the call to another member function such as get1, get2, etc. These can be private member functions so the client doesn't need to know that they exist.
    Danny Kalev

Similar Threads

  1. JDOM Classpath Help Required
    By kpandya in forum Java
    Replies: 5
    Last Post: 01-15-2006, 07:10 PM
  2. Help with class/applet
    By none_none in forum Java
    Replies: 17
    Last Post: 04-28-2005, 03:00 PM
  3. Replies: 5
    Last Post: 10-17-2002, 01:58 PM
  4. Replies: 205
    Last Post: 09-26-2001, 01:37 PM
  5. data source class in ActiveX DLL vs EXE -- URGENT
    By Dave Kraft in forum VB Classic
    Replies: 3
    Last Post: 10-10-2000, 04:15 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
HTML5 Development Center
 
 
FAQ
Latest Articles
Java
.NET
XML
Database
Enterprise
Questions? Contact us.
C++
Web Development
Wireless
Latest Tips
Open Source


   Development Centers

   -- Android Development Center
   -- Cloud Development Project Center
   -- HTML5 Development Center
   -- Windows Mobile Development Center