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
|
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 codeguru.com
Copyright Internet.com Inc., All Rights Reserved. |