DLL's and inheritence

Started by
17 comments, last by gimp 23 years, 1 month ago
Ok, guys I think I''m close,

I''ve read the tutorial suggested and null_pointers''s code sample. The code seems almost compilable. I have some problems with the Class I''l importing which I''m not sure are my of this new codes fault(simple due to the fact that I''ve never gotten this code to compile anyway).

Here are the problems:

1) warning C4273: ''CNULLRenderer::CNULLRenderer'' : inconsistent dll linkage. dllexport assumed.
This occurs on every function that has an implementation in the DLL file.

2) NULL Renderer.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) const CNULLRenderer::`vftable''" (__imp_??_7CNULLRenderer@@6B@)
Strangely however a similar message appears when I have no default constructor define in the base or derived class:
NULL Renderer.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall CNULLRenderer::CNULLRenderer(void)" (__imp_??0CNULLRenderer@@QAE@XZ)


Following is some source that should be relevent:
  ////////////////////////////////////////Interface.h Defines a base class object for all objects created in DLL''sclass IInterface{};typedef IInterface* (* allocate_type)();typedef void (* deallocate_type)(IInterface*);////////////////////////////////////////Renderer.h This file is included in each renderer implementation DLLclass CRenderer : public IInterface{public:...}/////////////////////////////////////////NULLRenderer.h This is a DLL''s include file.#ifdef EXPORT#define EXPORT __declspec(dllexport)#else#define EXPORT __declspec(dllimport)#endif#pragma warning (disable:4275) //An exported class was derived from a class that was not exported.#include "Renderer.h"class EXPORT CNULLRenderer : public CRenderer{public:	const bool Create(void);	const bool Destroy(void);};extern "C" {	IInterface* allocate() { return new CNULLRenderer; }	void deallocate(IInterface* p) { delete p; }};///////////////////////////////////////Library.h This file wraps library loading \ class access. For simplicity each DLL will only have master 1 class derived from IInterfaceclass CLibrary{public:	CLibrary();	~CLibrary();		IInterface* Load(const char* a_LibraryName);	void Unload(void);private:	void*		m_LibraryHandle;	IInterface*	m_InterfaceClass;};  


If someone is willing to help and this is not enough information to go on I''m happy to mail the file, zipped they should only be a few K

Thanks

Chris
Chris Brodie
Advertisement
(Look up "__declspec(dllexport)" in the MSDN docs, they explain it much better than I can. )

In your case, use __declspec(dllexport) in the DLL. This will create the export symbol table so GetProcAddress can find the function pointers from the names. Your EXE should not include headers or .lib files from the DLLs because you want to link them manually.

Here''s a sample I created to test this out.

In the Win32 Console Application:


// Dynamic Linking.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include
#include

class base { public: virtual void function() = 0; };

typedef base* (* allocate_type)();
typedef void (* deallocate_type)(base*);

int main(int argc, char* argv[])
{
HMODULE hDLL = LoadLibrary("Example");

allocate_type allocate = reinterpret_cast&ltallocate_type>(GetProcAddress(hDLL, "allocate"));
deallocate_type deallocate = reinterpret_cast&ltdeallocate_type>(GetProcAddress(hDLL, "deallocate"));

base* p = allocate();
p->function();
deallocate(p);

FreeLibrary(hDLL);
return 0;
}



In the Win32 DLL:


// Example.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include

class base { public: virtual void function() = 0; };
class derived : public base { public: virtual void function() { std::cout << "derived function called"; } };

extern "C" {
__declspec(dllexport) base* allocate() { return new derived; }
__declspec(dllexport) void deallocate(base* p) { delete p; }
};

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}


Note the only uses of __declspec(-anything-) are in the DLL, and only in the functions. Those function are the only point of communication between the EXE and the DLL; once the derived class has been instantiated, all communication with the DLL will go through the virtual function table as in: p->function().

The code works, I''ve tested it myself, and it correctly prints out "derived function called". Remember to place the compiled DLL in the project directory of the console application. Do not place it in the "Debug" or "Release" directories!

Good Luck,
- null_pointer
Thankyou VERY much null, this isn''t the first time you''ve taken the time to help me understand something very useful.
Chris Brodie
No problem, I''m just glad to explain a bit about those error messages. MSVC can be frustrating at times.
Konk...

Looks like I spoke too soon. When I first implemented this it worked fine, now on exit I get a crt debug error when calling Deallocate(delete) on the pointer. I have your example and it compiles fine. My engine code isn''t much different.

Can you, anyone, think of a reason why the pointer while valid would cause a debug message on deletion?

Once again many thanks?

Chris

Chris Brodie
Hmm...well, there could be several reasons causing an error on delete, and none of them seem to apply to this situation, unless it happens to be a simple typo or something in your code.

  • Calling delete on an invalid non-zero pointer.

  • Calling delete on memory allocated from a different heap.

  • Calling the wrong version of delete.

  • MSVC 4.0 had a rare bug which wouldn't let you delete objects created inside DLLs if the object had virtual functions.



Can you send the code to me?

(BTW, if you are calling delete on a base class pointer to a derived class, make sure the destructors are virtual! Otherwise, the derived class destructor may not be called. )


Edited by - null_pointer on March 20, 2001 3:06:41 PM
A common problem with linking to DLL''s at run time is that they often have separate heaps. This means that when you allocate memory in the DLL, and try and deallocate it from the main program, it will cause an error because the main application heap is trying to free memory it knows nothing about.

There are a couple of ways to fix this problem:

1)
Have a DeleteThis() function in each base class that simply is:
virtual void DeleteThis() { delete this; }

And instead of calling ''delete myObject'', do myObject->DeleteThis().

2)
I haven''t tried this, but I assume it would work.

For MSVC, use the compiler switch /MD or /MDd (you can change this in the link options for the project) to change the CRT heap to use the msvcrt.dll as the crt.


Hope this helps.

Oh good grief, I spend an hour pouring over the source code - why didn''t I think of that? I just had the same problem with delete and the run-time library a few weeks ago! Kudos to Phillip.

Change the projects to use the same run-time library. Open up the Project Settings and go to the C++ tab. Select Code Generation from the drop-down list box, and select "Multithreaded DLL" for the Release build and "Debug Multithreaded DLL" for the Debug build. If for some strange reason this doesn''t work, you can always go back to the defaults by finding the item with the asterisk (*).

This should also make your total app size smaller if you are using DLLs in your project, but probably not by much. For more information look up "MSVCRT.DLL" in the MSDN Library''s index.

Good Luck,
- null_pointer
Thnaks all. It''s now fixed though the first method of "DeleteThis()". I couldn''t get my application to compile when I changed the library type... for some reason it started complaining about basic_string being multiply defined...

Anyway... it''s now solved...

Thanks again

Chris
Chris Brodie

This topic is closed to new replies.

Advertisement