Explicitly Linking to Classes in DLL's

Sometimes Explicit Linking to DLL's is advantageous over implicit linking. For example, if at runtime the DLL is not found the application can display an error message and still continue. Explicit linking is also useful if you want users to provide a plugin for your application, in which case you could explicitly load the dll and call some predefined set of functions in it.

Explicit linking to global(non-member) C/C++ is quite easy. For example, suppose you wan't to call to a function ExportedFn in a dll. You can simply export the function like this (or through the def file):-

	
extern "C" _declspec(dllexport) 
 void ExportedFn(int Param1, char* param2);

The extern "C" linkage specification is required because otherwise C++ compiler generates a decorated name for the function and the function would not be exported with the name "ExportedFn" instead it would be exported something like "??ExportedFn@QAEX" . If this function resides in a DLL called DLL1.dll a client exe can call this function simply like this :-


HMODULE hMod = LoadLibrary("Dll1.dll");
typedef void (*PExportedFn)(int, char*);

PExportedFn pfnEF = (PExportedFn)GetProcAdress("ExportedFn");

pfnEF(1, "SomeString");

But what if you want to export a bunch of member functions of a C++ class and link to them explicitly. There are two problems. The first problem is that C++ member function names are decorated names (Specifying extern "C" does not help). The second problem is that C++ language specifications do not allow pointer to member functions to be converted into other types (later on I will present a simple way to do that). These two problems do restrict exporting of C++ classes in DLL's. In this article I will show some ways by which we could overcome these restrictions and explicitly load classes from dlls.

I am going to present two methods in this article and I will cover another method (delegation) in an other article.

The three methods which I am going to cover in this article are :-

  1. Using virtual functions table or vtable. This is the way COM operates.
  2. Using direct calls through GetProcAddress.

I will be taking the following example class for the purpose if this article.


class A
{
private:
 int m_nNum;	
public:	
 A();
 A(int n);
 virtual ~A();
 void SetNum(int n);
 int GetNum();
};

I have provided two different implementations of the class in two different DLLs. A client exe allows the user to enter the name of the dll from which the class should be loaded and accordingly output the results. You can download the sample project from this link The sample contains one workspace known as ExpClass.dsw, which has three projects one each for the two dlls and one for the client exe. The code demonstrates both the methods.

Exporting Class Using VTable

This method is the basis of COM. When we declare member function(s) of a class as virtual, compiler creates a table of all the virtual functions in the order in which they appear in the declaration. When an object of that class is created the first four bytes of the object point to that table. If we change the declaration of the class A to be :-


class A
{
private:
 int m_nNum;	
public:	
 A();
 A(int n);
 virtual ~A();
 virtual void SetNum(int n);
 virtual int GetNum();
};

Now a table is generated by the compiler which has the three virtual functions - the destructor, SetNum and GetNum. (You can actually observe that a virtual function table is created if you list the assembly with the source code. You can change the project options for that).

Now the object needs to be created in the dll. Since we are going to link only explicitly we need some global exported functions that create an object of the class through operator new. Since there are two constructors we can create two functions : CreateObjectofA() and CreateObjectofA1(int) and export them. Finally the exe can use the object as


typedef A* (*PFNCreateA1)();

PFNCreateA1 pfnCreateA1 = 
 (PFNCreateA1)GetProcAddress(hMod, TEXT("CreateObjectofA1"));

A* a = (pfnCreateA1)();
a->SetNum(1);
 _tprintf(TEXT("Value of m_nNum in a is %d\n"),a->GetNum());
	
delete a;

The important thing to note is that CreateObjectofA creates the the class using operator new this allows the client exe to safely call opeartor delete.


extern "C" __declspec(dllexport) A* CreateObjectofA1()
{
 return new A();
}

This method is very useful if you want users to make plugins for your applications. The drawback of this method is that the memory for the class must always be allocated in the dll. If the client wants to create an object by allocating memory in a different way it can't do so.

The next method involves obtaining the functions directly through GetProcAddress and calling the functions. The trick is to convert the FARPROC returned by GetProcAddress into C++ pointer to member functions. Fortuantely through C++ facility of templates and union this could be don very easily. All that needs to be done is to define a function like this


template
Dest force_cast(Src src)
{
 union
 {
  Dest d;
  Src s;
 } convertor;

 convertor.s = Src;
 return convertor.d;
}

The above function lets us cast variables of any different types and is more powerful then reinterpret_cast. For example, if we define a pointer type


typedef void (A::*PSetNum)(int);

We can convert a pointer fp which is of type FARPROC to PSetNum simple by using.


FARPROC fp;
.
.

PSetNum psn = force_cast<PSetNum>(fp);

The above operation is not possible through reinterpret_cast or C-Style cast.

Having found a way to convert FARPROC to a pointer to member, let's see ways to export C++ class member functions through friendly names. This can be done through .def files.

The first step is to find the decorated names of each of the functions that need to be exported, this can be done through either through map file or by generating assembly listing. Then the functions can be exported by friendly names through the following .def file syntax :-


EXPORTS
 ConstructorOfA1 = ??0A@@QAE@XZ        PRIVATE
 ConstructorOfA2 = ??0A@@QAE@H@Z       PRIVATE
 SetNumOfA       = ?SetNum@A@@UAEXH@Z  PRIVATE
 GetNumOfA       = ?GetNum@A@@UAEHXZ   PRIVATE	
 DestructorOfA   = ??1A@@UAE@XZ        PRIVATE

Now the functions are exported through much more friendly names. Now here is the way by which these are called


 typedef void (A::*PfnConstructorOfA1)();
 typedef void (A::*PfnConstructorOfA2)(int);
 typedef void (A::*PfnDestructorOfA)();
 typedef void (A::*PfnSetNumOfA)(int);
 typedef int  (A::*PfnGetNumOfA)();
	
 A* a1 = (A*)_alloca(sizeof(A));

 PfnConstructorOfA1 pfnConsA = 
  force_cast<PfnConstructorOfA1>(GetProcAddress(hMod, 
   TEXT("ConstructorOfA1")));

 (a1->*pfnConsA)();

 PfnSetNumOfA pfnSetNumA = 
  force_cast<PfnSetNumOfA>(GetProcAddress(hMod, 
   TEXT("SetNumOfA")));

 (a1->*pfnSetNumA)(1);
	
 PfnGetNumOfA pfnGetNumA = 
  force_cast<PfnGetNumOfA>(GetProcAddress(hMod, 
   TEXT("GetNumOfA"))); 

 _tprintf(TEXT("Value of m_nNum in a is %d\n"),(a1->*pfnGetNumA)());

 PfnDestructorOfA pfnDestA =  
  force_cast<PfnDestructorOfA>(GetProcAddress(hMod, 
   TEXT("DestructorOfA")));

 (a1->*pfnDestA)();

An interesting things to note here is that constructors and destructors are both being called explicitly. This is perhaps only way by which class constructors could be invoked explicitly. The other point to observe is that the object has been allocated memory over the stack through function alloca (if want to allocate memory on heap you need to use malloc), this is because allocating memory through new or by just decalaring an object of type A the constructor of A is automatically called. We don't want't this because the constructor of A is implemented in the Dll and we have not implicitly linked to the dll. You need not explicitly call the constructor and destructor instead you could implement the constructor in you exe file as :-


A::A()
{
 static PfnConstructorOfA1 pfnConsA1 = 
  force_cast<PfnConstructorOfA1>
  (GetProcAddress(ClassLoader<A>::s_hMod, 
   TEXT("ConstructorOfA1")));

 (this->*pfnConsA1)();
}

A::~A()
{
 static PfnDestructorOfA pfnDestA = 
  force_cast<PfnDestructorOfA>
  (GetProcAddress(ClassLoader<A>::s_hMod, 
   TEXT("ConstructorOfA1")));

 (this->*pfnDestA)();
}

The above two implementations just delegate to the actual function in the dll at the same time allow you two declare and use an object of A in normal fashion. I will cover a better form of delegation in the next article. An important point to mention here is that you need not implement the functions SetNum, GetNum etc. if you only call them through pointers. The sample attached explains all the methods.

Downloads

Download demo project - 8.8 Kb


Comments

  • other types than "void"?

    Posted by kpeo on 11/18/2011 10:05am

    well, but how about other types than "void"? for example "typedef classA* (*classFnc)()"? it wouldn't work.

    Reply
  • It works

    Posted by ram2612 on 08/20/2011 12:37pm

    Thanks a lot !!!

    Reply
  • error during compilation

    Posted by Legacy on 12/26/2003 12:00am

    Originally posted by: Thomas

    Hi,
    I 've got the following error during the compilation (using visual c++ 5) :
    error C2059: syntax error : 'string'

    This error seems to come from the extern "C".
    Does anyone can help me ??????

    Reply
  • !! doesn't work in a real context !!

    Posted by Legacy on 02/19/2003 12:00am

    Originally posted by: jp

    In the exemple code, the c++ code for the class A to export is compiled with the main program so there is no problem during the lonking.

    But what we want is to have DLLs that work as blackboxes which means that the c code (not the header) is unknown to the main program. This would be the common scenario for plug-ins. you have only access to the header file (here "ExpClass.h").

    If we change the project exemple moving the "ImplA.cpp" code to one (or the two dll), then there is a link problem because the main program does not find the object any longer.

    If we include the ".lib" file corresponding to the dll in the folder of the application that contains all the object files, it links...but we need the lib file during the comilation of the application...so it is not like a plugin where we just have the dll.


    So how can we successfully link when the main program does not have access to either the .cpp, the .obj or .lib files of the class to import/export?

    Thanks for comments and answers :))

    Reply
  • Thanks very so much!!!!

    Posted by Legacy on 10/25/2002 12:00am

    Originally posted by: Rodrigo

    Thanks very much for so a good article. It really solve a serious issue I got.
    I'm implementing a Web Service in .NET and needed to use some class library written in C++ Builder. The problem was how to use those classes in C#. Well, I write a C++ class importing the classes from C++ Builder using the guidelines in this article. Then, write a classes in Managed C++.NET to wrap those and finally connecting to the C# World. One more time, thanks.

    Reply
  • creating plugin system, need all dlls in the same memory space

    Posted by Legacy on 10/17/2002 12:00am

    Originally posted by: Jeremy D

    I am new to dll programming so bare with me.
    Windows appears to treat dlls as shared memory spaces so that multiple programs can access the same dll. This design prohibits me from allocating memory in the main program and deallocating it in the dll, visa versa. I am developing a plugin system however so that one dll is only used by one program once. The design also calls for memory allocations to ocur across the two separate heaps. Is there anyway i can load a dll in this fashion? I understand there are globalallocs functions, is this my only way? What are the consequences of using these functions?

    Reply
  • STL allocation problems

    Posted by Legacy on 10/17/2002 12:00am

    Originally posted by: Jeremy D

    Heres my problem:
    
    The main propgram calls into a dll via a virtual function.
    The dll contains an instance of the class that is derievied from some base class that the main program is aware of allowing me to do the former. this works great.
    The propblem areas if the dll class calls any function from the main program that has STL add a new entry into an STL::MAP. In the pseudo-code below, when the DLL calls AddObject() an access violation occurs when STL adds the CObject pointer

    For instance:

    /// Main Program /////////////////////////
    Class CBase
    {
    CBase();
    virtual ~CBase();
    virtual DoIt(CManager *man);
    }

    class CObject;

    Class CManager
    {
    CManager();
    ~CManager();
    DoSomething();
    AddObject();
    private:
    stl::map<int, CObject*> object_list;
    }

    CManager::AddObject()
    {
    CObject *co = new CObject;

    object_list[some_index] = co; // this line will fail
    // access violation
    }
    CManager::DoSomething()
    {
    // get an instance of the Derive class from the dll
    // d

    CBase *b = d;
    b->DoIt(this);
    }


    /// DLL /////////////////////////
    Class Derive
    {
    Derive();
    virtual ~Derive();
    virtual DoIt(CManager &man);
    }

    Derive::DoIt(CManager &man)
    {
    man->AddObject();
    }

    Reply
  • Help ... Why I got access violation ?

    Posted by Legacy on 09/26/2002 12:00am

    Originally posted by: Lia

    Hi all,
    
    

    I got problem in that when I try to execute the application program, there is an error of access violation.

    Does anybody have any suggestion on how I can solve the problem ? Is it because I have not allocated the space for the stack ?

    Below is a part of my application program:
    For your info, I need to import several functions (around 5 functions) from the DLL file
    main()
    {

    HINSTANCE hMod = LoadLibrary("sc32dll.dll");

    typedef short(*PInitDriver)(SCHANDLE*, short);
    typedef short(*PCardPresent)(SCHANDLE*, unsigned char);

    ......

    PInitDriver pInit = (PInitDriver)GetProcAddress(hMod, "InitDriver");
    PCardPresent pCardP = (PCardPresent)GetProcAddress(hMod, "CardPresent");
    ....

    InitReturn = pInit(&handle, portno[0]);

    ListReaderReturn = pList(portno);

    ....

    FreeLibrary(hMod);
    }

    Thank You. Your help will be most appreciated.

    Lia




    Reply
  • Thanks!

    Posted by Legacy on 01/07/2002 12:00am

    Originally posted by: Morgan Prakashpalan

    Thanks a lot! This was real helpful!

    -Morgan

    Reply
  • OK heres a challenge

    Posted by Legacy on 12/18/2001 12:00am

    Originally posted by: OTIS spunkmeyer

    What if I have a C++ DLL that do not have source for. How do I get at the C++ functions? I want to write a C wrapper. Any Idea's?

    OTIS

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • The open source cloud computing project OpenStack has come a long way since NASA and Rackspace launched it in 2010. Backed by leading technology infrastructure providers including Cisco, Dell, EMC, HP, IBM, Intel, and VMware, OpenStack underpins significant workloads at an increasingly diverse set of organizations, including BWM, CERN, Comcast, eBay, and Wal-Mart. For CIOs engaged in broader programs to win, serve, and retain customers -- and refocus business technology (BT) spend -- a planned and pragmatic …

  • For many organizations, moving enterprise applications to the public cloud can be a very attractive proposition, but planning the best way to move your applications is mission–critical. As an alternative to the costly option of re–architecting the application for a cloud environment, you can follow a "lift and shift" model that's significantly cheaper and almost always a lot quicker. In order to have a successful "lift and shift" migration, read this white paper to learn a few rules you should …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date