Click here to Skip to main content
15,896,432 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi,

I'm having trouble exporting classes in a diamond structure from a DLL:

C++
#ifdef DLLDLL_EXPORTS
#define DLLDLL_API __declspec(dllexport)
#else
#define DLLDLL_API __declspec(dllimport)
#endif

class DLLDLL_API CBase
{
...
};

class DLLDLL_API CDerivedA : virtual public CBase
{
...
};

class DLLDLL_API CDerivedB : virtual public CBase
{
...
};

class DLLDLL_API CDerivedAB : public CDerivedA, public CDerivedB
{
...
};


The functions, constructors and destructors of the classes give a linking error.

I'm capable of exporting normal and derived classes but I can't figure out how to do this, so I hope someone can help if this is even possible.

Edit:

CTest.h:
C++
#ifdef DLLDLL_EXPORTS
#define DLLDLL_API __declspec(dllexport)
#else
#define DLLDLL_API __declspec(dllimport)
#endif

class DLLDLL_API CBase 
{
private:
	int identifier;

protected:
		  CBase (int ID);
	TCHAR szText [40];
};

class DLLDLL_API CDerivedA : virtual public CBase
{
public:
		 CDerivedA (int ID);
	void SetTextA (TCHAR* sz);
	void ShoutA   (HWND hwnd);
};

class DLLDLL_API CDerivedB : virtual public CBase
{
public:
		 CDerivedB (int ID);
	void SetTextB (TCHAR* sz);
	void ShoutB   (HWND hwnd);
};

class DLLDLL_API CDerivedAB : public CDerivedA, public CDerivedB
{
public:
	     CDerivedAB (int ID);
	void SetTextAB ();
	void ShoutAB   (HWND hwnd);
};


CTest.cpp
C++
#include "CTest.h"

CBase::CBase (int ID)
{
	identifier = ID;
}

CDerivedA::CDerivedA (int ID) : CBase (ID)
{}

void CDerivedA::SetTextA (TCHAR* sz)
{
	_tcscpy (szText, sz);
}

void CDerivedA::ShoutA (HWND hwnd)
{
	MessageBox (hwnd, szText, TEXT ("DerivedA:"), MB_OK);
}

CDerivedB::CDerivedB (int ID) : CBase (ID)
{}

void CDerivedB::SetTextB (TCHAR* sz)
{
	_tcscpy (szText, sz);
}

void CDerivedB::ShoutB (HWND hwnd)
{
	MessageBox (hwnd, szText, TEXT ("DerivedB:"), MB_OK);
}

CDerivedAB::CDerivedAB (i<pre lang="text">
nt ID) : CBase (ID), CDerivedA (ID), CDerivedB (ID)
{}

void CDerivedAB::SetTextAB ()
{
SetTextA (TEXT ("Text A"));
SetTextB (TEXT ("Text B"));
}

Errors:
Error	13	error LNK2001: unresolved external symbol "__declspec(dllimport) const CDerivedA::`vbtable'" (__imp_??_8CDerivedA@@7B@)	J:\Programming\White Wings\DLL - General\CTest.obj
Error	14	error LNK2001: unresolved external symbol "__declspec(dllimport) const CDerivedB::`vbtable'" (__imp_??_8CDerivedB@@7B@)	J:\Programming\White Wings\DLL - General\CTest.obj
Error	15	error LNK2001: unresolved external symbol "__declspec(dllimport) const CDerivedAB::`vbtable'{for `CDerivedB'}" (__imp_??_8CDerivedAB@@7BCDerivedB@@@)	J:\Programming\White Wings\DLL - General\CTest.obj
Error	16	error LNK2001: unresolved external symbol "__declspec(dllimport) const CDerivedAB::`vbtable'{for `CDerivedA'}" (__imp_??_8CDerivedAB@@7BCDerivedA@@@)	J:\Programming\White Wings\DLL - General\CTest.obj


Edit (with destructors):
C++
class DLLDLL_API CBase 
{
private:
	int identifier;

protected:
	 	  CBase (int ID);
	virtual  ~CBase (){};
	TCHAR szText [40];
};

class DLLDLL_API CDerivedA : virtual public CBase
{
public:
		 CDerivedA (int ID);
		~CDerivedA (){};
	void SetTextA (TCHAR* sz);
	void ShoutA   (HWND hwnd);
};

class DLLDLL_API CDerivedB : virtual public CBase
{
public:
		 CDerivedB (int ID);
		~CDerivedB (){};
	void SetTextB (TCHAR* sz);
	void ShoutB   (HWND hwnd);
};

class DLLDLL_API CDerivedAB : public CDerivedA, public CDerivedB
{
public:
	     CDerivedAB (int ID);
	    ~CDerivedAB (){};
	void SetTextAB ();
	void ShoutAB   (HWND hwnd);
};


Errors:
Error	73	error LNK2019: unresolved external symbol "__declspec(dllimport) const CBase::`vftable'" (__imp_??_7CBase@@6B@) referenced in function "protected: __thiscall CBase::CBase(int)" (??0CBase@@IAE@H@Z)	J:\Programming\White Wings\DLL - General\CTest.obj
Error	74	error LNK2019: unresolved external symbol "__declspec(dllimport) const CDerivedA::`vftable'" (__imp_??_7CDerivedA@@6B@) referenced in function "public: __thiscall CDerivedA::CDerivedA(int)" (??0CDerivedA@@QAE@H@Z)	J:\Programming\White Wings\DLL - General\CTest.obj
Error	75	error LNK2019: unresolved external symbol "__declspec(dllimport) const CDerivedA::`vbtable'" (__imp_??_8CDerivedA@@7B@) referenced in function "public: __thiscall CDerivedA::CDerivedA(int)" (??0CDerivedA@@QAE@H@Z)	J:\Programming\White Wings\DLL - General\CTest.obj
Error	76	error LNK2019: unresolved external symbol "__declspec(dllimport) const CDerivedB::`vftable'" (__imp_??_7CDerivedB@@6B@) referenced in function "public: __thiscall CDerivedB::CDerivedB(int)" (??0CDerivedB@@QAE@H@Z)	J:\Programming\White Wings\DLL - General\CTest.obj
Error	77	error LNK2019: unresolved external symbol "__declspec(dllimport) const CDerivedB::`vbtable'" (__imp_??_8CDerivedB@@7B@) referenced in function "public: __thiscall CDerivedB::CDerivedB(int)" (??0CDerivedB@@QAE@H@Z)	J:\Programming\White Wings\DLL - General\CTest.obj
Error	78	error LNK2019: unresolved external symbol "__declspec(dllimport) public: virtual __thiscall CDerivedA::~CDerivedA(void)" (__imp_??1CDerivedA@@UAE@XZ) referenced in function __unwindfunclet$??0CDerivedAB@@QAE@H@Z$0	J:\Programming\White Wings\DLL - General\CTest.obj
Error	79	error LNK2019: unresolved external symbol "__declspec(dllimport) protected: virtual __thiscall CBase::~CBase(void)" (__imp_??1CBase@@MAE@XZ) referenced in function __unwindfunclet$??0CDerivedAB@@QAE@H@Z$0	J:\Programming\White Wings\DLL - General\CTest.obj
Error	80	error LNK2019: unresolved external symbol "__declspec(dllimport) const CDerivedAB::`vftable'" (__imp_??_7CDerivedAB@@6B@) referenced in function "public: __thiscall CDerivedAB::CDerivedAB(int)" (??0CDerivedAB@@QAE@H@Z)	J:\Programming\White Wings\DLL - General\CTest.obj
Error	81	error LNK2019: unresolved external symbol "__declspec(dllimport) const CDerivedAB::`vbtable'{for `CDerivedB'}" (__imp_??_8CDerivedAB@@7BCDerivedB@@@) referenced in function "public: __thiscall CDerivedAB::CDerivedAB(int)" (??0CDerivedAB@@QAE@H@Z)	J:\Programming\White Wings\DLL - General\CTest.obj
Error	82	error LNK2019: unresolved external symbol "__declspec(dllimport) const CDerivedAB::`vbtable'{for `CDerivedA'}" (__imp_??_8CDerivedAB@@7BCDerivedA@@@) referenced in function "public: __thiscall CDerivedAB::CDerivedAB(int)" (??0CDerivedAB@@QAE@H@Z)	J:\Programming\White Wings\DLL - General\CTest.obj
Error	83	error LNK1120: 10 unresolved externals	J:\Programming\White Wings\Debug\DLL - General.dll
Error	106	error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall CDerivedA::`vbase destructor'(void)" (__imp_??_DCDerivedA@@QAEXXZ) referenced in function "void __cdecl `long __stdcall WndProc(struct HWND__ *,unsigned int,unsigned int,long)'::`2'::`dynamic atexit destructor for 'cDeriveda''(void)" (??__FcDeriveda@?1??WndProc@@YGJPAUHWND__@@IIJ@Z@YAXXZ)	J:\Programming\White Wings\Testing\main.obj
Error	107	error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall CDerivedAB::`vbase destructor'(void)" (__imp_??_DCDerivedAB@@QAEXXZ) referenced in function "void __cdecl `long __stdcall WndProc(struct HWND__ *,unsigned int,unsigned int,long)'::`2'::`dynamic atexit destructor for 'cDerivedAB''(void)" (??__FcDerivedAB@?1??WndProc@@YGJPAUHWND__@@IIJ@Z@YAXXZ)	J:\Programming\White Wings\Testing\main.obj
Error	108	error LNK1120: 2 unresolved externals	J:\Programming\White Wings\Debug\Testing.exe	1
Posted
Updated 29-Sep-11 6:28am
v4
Comments
Richard MacCutchan 28-Sep-11 12:07pm    
Let us see the exact text of your error message. The above definitions look like they should work.
Richard MacCutchan 28-Sep-11 15:04pm    
What is the purpose of the virtual keyword in the above class definitions?
TimGalant 28-Sep-11 15:05pm    
I want the base class to be the same for CDerivedAB instead of one for CDerivedA and one for CDerivedB, so that's why I use virtual.
Richard MacCutchan 28-Sep-11 15:55pm    
Sorry but I can't find any useful reference to the symbol that seems to be causing the problem. Do these errors disappear if you make you classes non-virtual?
TimGalant 28-Sep-11 15:57pm    
No, if they're non-virtual they're simple derived classes which I'm able to export correctly.

I'm not entirely sure about how to export an entire class, but something that worked for me is to explicitely declare each member function as dllexport. AFAIK you can remove the dllexport qualifier from the class then (I could, but maybe you still need it if you want to create instances of a class).

So you'd need to do sth like this:
C++
class CBase {
public:
   virtual void DLLDLL_API foo();
   virtual void DLLDLL_API bar() const;
//...
};


P.S.: after seeing your error messages I realize that the problem I had was not the same as yours. Still, I wonder if my suggestion could work nonetheless; did you try?
 
Share this answer
 
v2
I think the problem is that none of your class have virtual functions. Thy to add a virtual destructor to CBase class.

Since there is no virtual function the vtable is probably not created and not exported but is is required because of virtual base class.
 
Share this answer
 
Cannot see any problem:
1. enshure the output path for DLL and LIB file is the path you load the DLL and include the LIB in your project (usually the same as the EXE output path).
2. include the header and the LIB with correct filename (perhaps including path).
3. first project for DLL and LIB contains: exportable.h and exportable.cpp
4. the exe file is one DerivedClass.cpp
example:
LIB and DLL project
content exportable.h
C++
// this: exportable.h
// impl: exportable.cpp

#ifdef BASECLASSDLL_EXPORTS
// you have to define this in your DLL project settings
#define DLLDLL_API __declspec(dllexport)
#else
#define DLLDLL_API __declspec(dllimport)
#endif

class DLLDLL_API CBase 
{
private:
  int identifier;
 
protected:
       CBase (int ID);
  virtual  ~CBase (){}
  TCHAR szText [40];
};
 
class DLLDLL_API CDerivedA : virtual public CBase
{
public:
     CDerivedA (int ID);
    ~CDerivedA (){};
  void SetTextA (TCHAR* sz);
  void ShoutA   (HWND hwnd);
};

class DLLDLL_API CDerivedB : virtual public CBase
{
public:
     CDerivedB (int ID);
    ~CDerivedB (){};
  void SetTextB (TCHAR* sz);
  void ShoutB   (HWND hwnd);
};

class DLLDLL_API CDerivedAB : public CDerivedA, public CDerivedB
{
public:
       CDerivedAB (int ID);
      ~CDerivedAB (){}
  void SetTextAB (TCHAR* sz);
  void ShoutAB   (HWND hwnd);
};

content of exportable.cpp
C++
// this: exportable.cpp
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
int FAR PASCAL DllMain(HMODULE h,DWORD r,void*){ return 1; }

#include <tchar.h>
#include "exportable.h"

//////////////////////
CBase::CBase(int ID)
{
  identifier = ID;
  szText[0] = 0;
}


//////////////////////
CDerivedA::CDerivedA(int ID)
  :CBase (ID)
{
}

void CDerivedA::SetTextA (TCHAR* sz)
{
  _tcscpy_s(szText,sizeof(szText)/sizeof(szText[0]),sz?sz:__T(""));
}

void CDerivedA::ShoutA   (HWND hwnd)
{
  MessageBox (hwnd, szText, TEXT ("CDerivedA:"), MB_OK);
}

//////////////////////
CDerivedB::CDerivedB(int ID)
  :CBase (ID)
{
}

void CDerivedB::SetTextB (TCHAR* sz)
{
  _tcscpy_s(szText,sizeof(szText)/sizeof(szText[0]),sz?sz:__T(""));
}

void CDerivedB::ShoutB   (HWND hwnd)
{
  MessageBox (hwnd, szText, TEXT ("DerivedB:"), MB_OK);
}

//////////////////////
CDerivedAB::CDerivedAB (int ID)
  :CBase(ID),CDerivedA(ID),CDerivedB(ID)
{
}

void CDerivedAB::SetTextAB (TCHAR* sz)
{
  SetTextA(sz);
  SetTextB(sz);
}

void CDerivedAB::ShoutAB   (HWND hwnd)
{
  MessageBox (hwnd, szText, TEXT ("CDerivedAB:"), MB_OK);
}

the EXE file that import all these things. Output path is .\run.
content of DerivedClass.cpp
C++
// this: DerivedClass.cpp

#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tchar.h>

#include "<replace this with your path>\DerivedClass\BaseClassDLL\exportable.h"
#pragma comment (lib,"<replace this with your path>\\DerivedClass\\run\\BaseClassDLL.lib")

int FAR PASCAL _tWinMain(HINSTANCE h,HINSTANCE p,LPTSTR c,int sw)
{
  CDerivedAB  ab(1);
  ab.SetTextAB(__TEXT("Hello world"));
  ab.ShoutAB(0);
  return 0;
}

Regards.

[edit] some useless fragments removed.
 
Share this answer
 
v2
Comments
TimGalant 30-Sep-11 13:43pm    
I've linked the dll as one of the projects in my solution. Normal classes export right, so I don't thinks it's the stuff "around" the coding.
I've restructured my code and I did manage to make the code work in another way:

CTest.h:
C++
class DLLDLL_API CDerivedB;

class DLLDLL_API CBase 
{
private:
	friend class CDerivedB;
	int identifier;

protected:
	 	  CBase (int ID);
	TCHAR szText [40];
};

class DLLDLL_API CDerivedA : public CBase
{
public:
	     CDerivedA (int ID);
	void SetTextA (TCHAR* sz);
	void ShoutA   (HWND hwnd);
};

class DLLDLL_API CDerivedB
{
private:
	CBase* pcBase;
public:
		 CDerivedB (int ID, CBase* pcCBase = NULL);
	void SetTextB (TCHAR* sz);
	void ShoutB   (HWND hwnd);
};

class DLLDLL_API CDerivedAB : public CDerivedA, public CDerivedB
{
public:
	     CDerivedAB (int ID);
	void SetTextAB ();
	void ShoutAB   (HWND hwnd);
};


CTest.cpp:
C++
#include "CTest.h"

CBase::CBase (int ID)
{
	identifier = ID;
}

CDerivedA::CDerivedA (int ID) : CBase (ID)
{}

void CDerivedA::SetTextA (TCHAR* sz)
{
	_tcscpy (szText, sz);
}

void CDerivedA::ShoutA (HWND hwnd)
{
	MessageBox (hwnd, szText, TEXT ("DerivedA:"), MB_OK);
}

CDerivedB::CDerivedB (int ID, CBase* pcCBase)
{
	if (pcCBase != NULL)
		pcBase = pcCBase;
	else
		pcBase = new CBase (ID);
}

void CDerivedB::SetTextB (TCHAR* sz)
{
	_tcscpy (pcBase->szText, sz);
}

void CDerivedB::ShoutB (HWND hwnd)
{
	MessageBox (hwnd, pcBase->szText, TEXT ("DerivedB:"), MB_OK);
}

CDerivedAB::CDerivedAB (int ID) : CDerivedA (ID), CDerivedB (ID, this)
{}

void CDerivedAB::SetTextAB ()
{
	SetTextA (TEXT ("Text A"));
	SetTextB (TEXT ("Text B"));
}

void CDerivedAB::ShoutAB (HWND hwnd)
{
	ShoutA (hwnd); 
	ShoutB (hwnd);
}
<pre>
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900