Click to See Complete Forum and Search --> : dll


lindawqu
February 21st, 2002, 11:21 PM
Please tell me how to create c++ dll and how to use it after creation. Please give me a simple example so that I can compile it and run it. In this way, I can understand it easily. thanks

NMTop40
February 22nd, 2002, 05:00 AM
when I created a test DLL (Win32 DLL, no MFC) I got these compiler options. This is with no precompiled headers.

/nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TESTDLL_EXPORTS" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c
for debug and these

/nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "TESTDLL_EXPORTS" /Fo"Release/" /Fd"Release/" /FD /c

for release. If you are doing it in an C++ application that has a wizard (allows you to create a new project, determining at the same time the type of project you want).

---------------------------------------------
Laying out your DLL

The main feature I am going to discuss is the header file.
This is a single header file that users of the DLL include,
and it should expose the interface (and preferably nothing but
the interface. As long as you don't allow the user to see any
of the implementation, you can change it later without breaking
their programs).

You can give your DLL a C-interface, even if it is written in C++.
If you do that, you must put this in front of the functions

#ifdef __cplusplus
extern "C" {
#endif

// all your interface

#ifdef __cplusplus
}
#endif




This is not necessary for a C++ interface.

An important part of the DLL header file is to declare the functions
(or classes if a C++ interface) as exports. The best way to do this
is to insert

__declspec(dllimport)



As part of the declaration of the class or function. For a class this
goes between the word class and the name of the class. For a function
it goes after the return type and before the name of the function. There
is no need to use the word "extern" - it is optional for prototypes as
they are extern by default.

However, if you want to use the same header for your own code that builds
the DLL, you will have to use __declspec(dllexport) instead. You can do
this in one of two ways: either have two headers, or at the top of your
headers have the following preprocessor option;


#ifdef TESTDLL_EXPORTS
#define TESTDLL_API __declspec(dllexport)
#else
#define TESTDLL_API __declspec(dllimport)
#endif



If you want your DLL to also compile on UNIX as a shared object (UNIX
equivalent of DLL), you want another preprocessor option based on the
operating system, thus

#ifndef WIN32
// above
#else
#define TESTDLL_API // nothing, you don't need it for UNIX
// so you must define it to nothingness so your code compiles
#endif

Now for a simple test example class
[ccode]
class CalculatorImpl;

class TESTDLL_API Calculator
{
public:
class DivideByZeroException {};

Calculator( double lhs=0.0, double rhs=0.0 );
virtual ~Calculator();
double GetSum();
double GetMultiple();
double GetDifference();
double GetQuotient() throw DivideByZeroException;
double GetPower();
double LHS();
double RHS();
void LHS( double lhs );
void RHS( double rhs );

private:
CalculatorImpl * pImpl;
};



That is all the client needs to see. No implementation detail
except for one pointer of a type unknown to it. This pointer
will contain the actual values of lhs and rhs, and will also
contain all the same functions. You body of class Calculator
will invoke it like this:

Calculator::Calculator( double lhs, double rhs )
{
pImpl = new CalculatorImpl( lhs, rhs );
}

Calculator::~Calculator()
{
delete pImpl;
}

// other function example
double Calculator::GetSum()
{
return pImpl->GetSum();
}



You may ask, why go to all this effort and not just put lhs and
rhs into the private section. I will tell you.

Normally your class will be more complex than this. Say you want
to have a method in your class to persist the data. So you may
want to provide a function persist(). How your class stores the
data is an implementation detail you neither want nor need to
reveal. However you will probably find that in order to implement
this function, you are going to need specifics in your private
section. While you might get away with more defined-only class
names, that may not be enough, and the level of coupling will go
up. You'll find your users running into problems that really are
nothing to do with them because it is your implementation and
not theirs.

You may also notice that I made the destructor virtual. You or a
user may wish to add more functionality. If that happens you can
derive a class from it.

There are one or two other things you might want to put in a private
section, but these are simply implementations of standard design
patterns. For example, if you want users only to be able to create
an instance on the heap, make your constructor protected (or private
to allow no derived classes) and provide a static Create() function.
Also if you want to implement as a singleton, this is done using a
private or protected constructor and usually (for me) a nested class
that implements auto-pointer behaviour.

Finally, you (and others) may note that I didn't even make the
implementation pointer an auto_ptr (or any other version of it).
While templates are very useful they do increase the level of
coupling and you may well find that a compiler complains that
it doesn't know how to delete an undefined class CalculatorImpl.
This is because auto_ptr hidden away deletes the pointer it is
pointing to, and your compiler would see that and want to know
how it's done.





The best things come to those who rate

Andreas Masur
February 22nd, 2002, 06:22 AM
I just want to give some more points on how to export functions since I do prefer exporting by ordinals rather than using '__declspec'...

There are several ways of exporting functions from a dll. The simplest way to export functions from your DLL is to export them by name. This is what happens if you use the keyword __declspec(dllexport).

// Export a function
__declspec(dllexport) void __cdecl Function(void);

// Export a class member function
class __declspec(dllexport) CFoo
{
};



But you can instead export functions by ordinal. With this technique, you must use a module.definition file instead of __declspec(dllexport). To specify a function’s ordinal value, append its ordinal to the function name in the module-definition file.

A module-definition file is a text file that contains statements for exporting functions from a DLL. Since the linker provides equivalent command-line options for most of the statements in a .def file, a typical program for Win32 does not usually require a .def file.

The following keywords are defined for usage in a .def file:

NAME

This statement specifies a name for the main output file.


LIBRARY

This statement tells the linker to create a DLL. At the same time, the linker creates an import library. The library argument specifies the internal name of the DLL.


DESCRIPTION

This statement allow you to enter a short description.


STACKSIZE

This statement sets the size of the stack in bytes.


SECTIONS

This statement sets attributes for one or more sections in the image file. It can be used to override the default attributes for each type of section.


EXPORTS

This statement makes one or more definitions available as exports to other programs.

EXPORTS marks the beginning of a list of export definitions. Each definition must be on a separate line. The EXPORTS keyword can be on the same line as the first definition or on a preceding line. The .def file can contain one or more EXPORTS statements.

There are three methods for exporting a definition, listed in recommended order of use:

1. The __declspec(dllexport) keyword in the source code
2. An EXPORTS statement in a .DEF file
3. An /EXPORT specification in a LINK command


VERSION

This statement tells the linker to put a number in the header of the DLL.


A minimal .DEF file must contain the 'LIBRARY' and 'EXPORTS'. The following is an example of a typical .def file:

LIBRARY ImageData
DESCRIPTION Handles the image data

EXPORTS

;public: __thiscall CImageData::CImageData(void)
??0CImageData@@QAE@XZ @1

;public: virtual __thiscall CImageData::~CImageData(void)
??1CImageData@@UAE@XZ @2

;public: void __thiscall CImageData::Clear(void)
?Clear@CImageData@@QAEXXZ @3



The corresponding class will look as follows

class CImageData
{
public:
unsigned long m_ulImageWidth;
unsigned long m_ulImageHeight;
unsigned short m_usBitsPerPixel;
unsigned short m_usCompression;

CImageData();

virtual ~CImageData() { }

void Clear();
};



As you can see all three functions of the class will be exported. They have the ordinals one (constructor), two (destructor) and three ('Clear()'). Any application which will use this dll can access those functions just like they would be defined in the application itself. All you need to do is to include the header of the DLL and add the .lib file to your linker options.

By using a .def file you have the control over the export ordinals. If you add additional functions later you just give them higher ordinals than the old ones. That allows application which are using your dll by implicit linking to use the newer version without needing to be relinked. You can also export functions using the 'NONAME' attribute which only places the ordinal number in the exports table. If you have a large number of functions you want to export using this attribute can reduce the size of the dll file.

There are of course disadvantages as well using this approach. If you are exporting functions within a C++ file, you either have to place the decorated names in the .def file or use standard C linkage by using 'extern "C"'. In the latter the compiler won't do the name decoration...


Ciao, Andreas

"Software is like sex, it's better when it's free." - Linus Torvalds