How to write a DLL (to deepen understanding with command-line compilation)

Source: Internet
Author: User

Advantages of DLL

Simply put, DLLs have several advantages:

1) Save memory. The same software module, if reused in the form of source code, is compiled into different executable programs, and the binaries of these modules are repeatedly loaded into memory when the EXE is run. If you use a DLL, it is loaded only once in memory, and all processes that use the DLL share this block of memory (of course, a global variable in a DLL is copied by each process).

2) do not need to compile software system upgrade, if a software system uses a DLL, then the DLL is changed (the function name is unchanged), the system upgrade only need to replace this DLL, do not need to recompile the entire system. In fact, many of the software is upgraded in this way. For example, we often play the Star, Warcraft and other games such as the version upgrade.

3) DLL libraries can be used in a variety of programming languages, such as DLL written in C can be called in VB. This point on the DLL is not enough, so in the DLL based on the invention of COM technology, a better solution to a series of problems.

The simplest DLL

Before you start writing a DLL, you need a C + + compiler and linker, and close your IDE. Yes, turn off your VC and C + + Builder, and open the Notepad program you used to remember the phone. If you do not, you may not understand the true meaning of the DLL for life. I use the VC comes with the CL compiler and link linker, they are generally in the VC bin directory. (If you do not choose to register environment variables when installing the VC, then immediately add their paths to path bar) If you are still afraid to cry because you left the IDE, you can close this page and continue to see "VC Tech Insider" and other boring books.

The simplest DLL is no more difficult than the HelloWorld of C, as long as a DllMain function is included, including the objbase.h header file (a header file that supports COM technology). If you think this header file name is hard to remember, then use Windows. H can also. The source code is as follows: Dll_nolib.cpp

#include <objbase.h>

#include <iostream.h>

BOOL apientry DllMain (HANDLE hmodule, DWORD dwreason, void* lpreserved)

{

HANDLE G_hmodule;

Switch (Dwreason)

{

Case Dll_process_attach:

cout<< "Dll is attached!" <<endl;

G_hmodule = (hinstance) hmodule;

Break

Case Dll_process_detach:

cout<< "Dll is detached!" <<endl;

G_hmodule=null;

Break

}

return true;

}

Where DllMain is the entry function for each DLL, as in C's main function. DllMain with three parameters, Hmodule represents the instance handle of this DLL (ignore it, write a natural understanding of the Windows program), Dwreason represents the current state of the DLL, for example Dll_process_ Attach indicates that the DLL has just been loaded into a process, and Dll_process_detach indicates that the DLL has just been unloaded from a process. Of course there are states that represent loading into threads and unloading from threads, omitted here. The last parameter is a reserved parameter (currently associated with some state of the DLL, but rarely used).

As can be seen from the above program, DLL print "DLL is attached!" when DLL is loaded into a process When the DLL is unloaded from the process, the print "DLL is detached!" Statement.

The following two commands are required to compile the DLL:

CL/C Dll_nolib.cpp

This command will compile the CPP into an obj file, and if you do not use the/C parameter, CL will also attempt to continue to link obj to exe, but here is a DLL with no main function and therefore an error. It doesn't matter, continue using the link command.

Link/dll Dll_nolib.obj

This command generates Dll_nolib.dll.

Note that because the compile command is simpler, this article does not discuss NMAKE, which is interesting to use NMAKE, or to write a bat batch to compile the linked DLL.

Load DLL (explicitly called)

There are roughly two ways to use a DLL, explicit invocation and implicit invocation. This first describes the explicit invocation. Write a client program: Dll_nolib_client.cpp

#include <windows.h>

#include <iostream.h>

int main (void)

{

Load our DLL

HInstance hinst=::loadlibrary ("Dll_nolib.dll");

if (NULL! = hinst)

{

cout<< "DLL loaded!" <<endl;

}

return 0;

}

Note that the calling DLL uses the LoadLibrary function, whose arguments are the path and name of the DLL, and the return value is the handle to the DLL. Compile the linked client using the following command:

Cl Dll_nolib_client.cpp

and execute Dll_nolib_client.exe, get the following result:

Dll is attached!

DLL loaded!

Dll is detached!

The above results indicate that the DLL has been loaded by the client. However, it is only possible to load the DLL into memory and cannot find the functions in the DLL.

To view functions in a DLL using the DUMPBIN command

The DUMPBIN command can view the output function symbol name in a DLL and type the following command:

Dumpbin–exports Dll_nolib.dll

By looking at it, we found that Dll_nolib.dll did not output any functions.

How to define an output function in a DLL

In general there are two ways to add a DEF definition file that defines the function to be exported in the DLL, and the second is to include the __declspec (dllexport) keyword in front of the function to be exported in the source code.

DEF file

First write a DLL with output function, the source code is as follows: Dll_def.cpp

#include <objbase.h>

#include <iostream.h>

void Funcindll (void)

{

cout<< "Funcindll is called!" <<endl;

}

BOOL apientry DllMain (HANDLE hmodule, DWORD dwreason, void* lpreserved)

{

HANDLE G_hmodule;

Switch (Dwreason)

{

Case Dll_process_attach:

G_hmodule = (hinstance) hmodule;

Break

Case Dll_process_detach:

G_hmodule=null;

Break

}

return TRUE;

}

The def file for this DLL is as follows: Dll_def.def

;

; Dll_def module-definition File

;

LIBRARY Dll_def.dll

DESCRIPTION ' (c) 2007-2009 Wang Xuebin '

Exports

Funcindll @1 PRIVATE

You will find that the DEF syntax is simple, first of all the Library keyword, the name of the DLL, and then an optional keyword description, followed by the copyright and other information (not written can also); Finally, the EXPORTS keyword, Then write all the function names or variable names in the DLL that you want to output, then the @ and numbered numbers (from 1 to N) followed by the modifier.

Compile the DLL that links the def file with the following command:

CL/C Dll_def.cpp

Link/dll Dll_def.obj/def:dll_def.def

Call dumpbin again to view the generated Dll_def.dll:

Dumpbin–exports Dll_def.dll

The following results are obtained:

Dump of File Dll_def.dll

File Type:dll

section contains the following exports for Dll_def.dll

0 characteristics

46e4ee98 Time Date stamp Mon Sep 10 15:13:28 2007

0.00 Version

1 ordinal base

1 Number of functions

1 Number of names

Ordinal hint RVA name

1 0 00001000 Funcindll

Summary

. Data

Rdata.

Reloc.

6000. Text

Observe this line

1 0 00001000 Funcindll

It is found that the DLL outputs the function Funcindll.

To explicitly call a function in a DLL

Write a Dll_def.dll client program: Dll_def_client.cpp

#include <windows.h>

#include <iostream.h>

int main (void)

{

Define a function pointer

typedef void (* Dllwithlib) (void);

Define a function pointer variable

Dllwithlib Pffuncindll = NULL;

Load our DLL

HInstance hinst=::loadlibrary ("Dll_def.dll");

if (NULL! = hinst)

{

cout<< "DLL loaded!" <<endl;

}

Find the DLL's Funcindll function

Pffuncindll = (dllwithlib) GetProcAddress (hinst, "Funcindll");

Calling a function in a DLL

if (NULL! = Pffuncindll)

{

(*pffuncindll) ();

}

return 0;

}

There are two places to note, the first is the definition and use of function pointers, do not understand the C + + book to look at; the second is the use of GetProcAddress, the API is used to find the function address in the DLL, the first parameter is the DLL's handle, that is, the handle returned by LoadLibrary , the second parameter is the function name in the DLL, which is the function name that is output in the DUMPBIN (note that the function name here refers to the compiled function name, not necessarily the name of the functor in the DLL source code).

Compiling links to this client program, and executing will get:

DLL loaded!

Funcindll is called!

This indicates that the client successfully called the function Funcindll in the DLL.

__declspec (dllexport)

Writing def for each DLL appears to be cumbersome, and the use of DEF is less, and more so, using __declspec (dllexport) to define the DLL's output function in the source code.

DLL, remove the Def file, and precede each function to be output with a declaration __declspec (dllexport), for example:

__declspec (dllexport) void Funcindll (void)

This provides a DLL source program Dll_withlib.cpp, and then compiles the link. Links do not need to specify/DEF: parameters, directly add/dll parameters,

CL/C Dll_withlib.cpp

Link/dll Dll_withlib.obj

Then use the DUMPBIN command to view and get:

1 0 00001000 [email protected] @YAXXZ

It is known that the compiled function is named [email protected] @YAXXZ, not funcindll, because the C + + compiler changes the function name based on the consideration of function overloading, so that when you use explicit invocation, you must also use the changed function name. This obviously brings trouble to the customer. To avoid this behavior, you can use the extern "C" directive to command the C + + compiler to name the function in the form of a compiler. The modified function declaration is:

extern "C" __declspec (dllexport) void Funcindll (void)

DUMPBIN command Result:

1 0 00001000 Funcindll

This way, an explicit call can only be accomplished by looking for a function named Funcindll.

extern "C"

Using the extern "C" keyword is actually equivalent to a compiler's switch, which compiles the C + + language's functions into the name of the function in the language. That is, keep the compiled function symbol name equal to the function name in the source code.

Implicitly invoking DLLs

Explicit invocation is very complex, LoadLibrary every time, and each function must use GetProcAddress to get a function pointer, which is a problem for a large number of clients using DLL functions. Implicit invocation can be used as a function in a DLL like the C function library, which is very convenient and quick to use.

The following is an example of an implicit invocation: A DLL contains two files Dll_withlibandh.cpp and dll_withlibandh.h.

The code is as follows: DLL_WITHLIBANDH.H

extern "C" __declspec (dllexport) void Funcindll (void);

Dll_withlibandh.cpp

#include <objbase.h>

#include <iostream.h>

#include "dll_withlibandh.h"//See no, this is the header file we added

extern "C" __declspec (dllexport) void Funcindll (void)

{

cout<< "Funcindll is called!" <<endl;

}

BOOL apientry DllMain (HANDLE hmodule, DWORD dwreason, void* lpreserved)

{

HANDLE G_hmodule;

Switch (Dwreason)

{

Case Dll_process_attach:

G_hmodule = (hinstance) hmodule;

Break

Case Dll_process_detach:

G_hmodule=null;

Break

}

return TRUE;

}

Compile link command:

CL/C Dll_withlibandh.cpp

Link/dll Dll_withlibandh.obj

When making an implicit call, you need to introduce a header file to the client and indicate the DLL's corresponding LIB file at link time (the DLL will generate a Lib file with the same name as the DLL whenever it has a function output). The functions in the DLL are then called as if they were called functions in the API library, and no explicit LoadLibrary and GetProcAddress are required. The most convenient to use. The client code is as follows: Dll_withlibandh_client.cpp

#include "dll_withlibandh.h"

Note the path, another way to load the DLL is Project | setting | In link settings

#pragma comment (lib, "Dll_withlibandh.lib")

int main (void)

{

Funcindll ();//Just so we can call the function in the DLL.

return 0;

}

Paired with __declspec (dllexport) and __declspec (dllimport)

The above method of implicit invocation is good, but there is a problem when calling objects and overloaded functions in the DLL. Because the output functions are decorated with extern "C", overloaded functions are definitely problematic because they will all be compiled into the same output symbol string (the C language is not supported for overloading).

In fact, it is possible not to use extern "C", when the function is compiled into C + + symbol string, for example ([email protected]@[email protected], [email protected] @YAXXZ), when the client is also C + +, It is also possible to invoke the correct implicit invocation.

At this point to consider a situation: if DLL1.CPP is the source, DLL2.CPP uses the function in DLL1, but at the same time DLL2 is also a DLL, but also to output some functions for Client.CPP use. So how do you declare all the functions in DLL2, which include the functions introduced from DLL1, and the functions you want to output. You need to use __declspec (dllexport) and __declspec (dllimport) at the same time. The former is used to modify the output function in this DLL, which is used to modify functions introduced from other DLLs.

All the source code includes dll1.h,dll1.cpp,dll2.h,dll2.cpp,client.cpp. The source code can be found in the downloaded package. You can compile the link and run the test.

It is worth paying attention to a coding method used in both DLL1 and DLL2, see DLL2.H

#ifdef Dll_dll2_exports

#define DLL_DLL2_API __declspec (dllexport)

#else

#define DLL_DLL2_API __declspec (dllimport)

#endif

Dll_dll2_api void FuncInDll2 (void);

DLL_DLL2_API void FuncInDll2 (int);

Defining macros Dll_dll2_exports and DLL_DLL2_API in this way in the header file ensures that functions on the DLL side are decorated with __declspec (dllexport), while the client's functions are decorated with __declspec (dllimport). Of course, remember to add the parameter/d "Dll_dll2_exports" when compiling the DLL, or simply add the # define dll_dll2_exports to the first line of the DLL's CPP file.

VC generated code is the same! It turns out that I copied it, hoho!.

Global variables and objects in a DLL

Solve the problem of overloaded functions, then the DLL in the global variables and objects are not a problem, just a little bit of syntax to note. As shown in the source code: dll_object.h

#ifdef Dll_object_exports

#define DLL_OBJECT_API __declspec (dllexport)

#else

#define DLL_OBJECT_API __declspec (dllimport)

#endif

Dll_object_api void Funcindll (void);

extern Dll_object_api int g_ndll;

Class Dll_object_api Cdll_object {

Public

Cdll_object (void);

Show (void);

Todo:add your methods here.

};

CPP files Dll_object.cpp as follows:

#define Dll_object_exports

#include <objbase.h>

#include <iostream.h>

#include "Dll_object.h"

Dll_object_api void Funcindll (void)

{

cout<< "Funcindll is called!" <<endl;

}

Dll_object_api int g_ndll = 9;

Cdll_object::cdll_object ()

{

cout<< "ctor of Cdll_object" <<endl;

}

Cdll_object::show ()

{

cout<< "function show in class Cdll_object" <<endl;

}

BOOL apientry DllMain (HANDLE hmodule, DWORD dwreason, void* lpreserved)

{

HANDLE G_hmodule;

Switch (Dwreason)

{

Case Dll_process_attach:

G_hmodule = (hinstance) hmodule;

Break

Case Dll_process_detach:

G_hmodule=null;

Break

}

return TRUE;

}

After compiling the link dumpbin, you can see the output of 5 symbols:

1 0 00001040 [email protected]@[email protected]

2 1 00001000 [email protected]@[email protected]@@z

3 2 00001020 [email protected] @YAXXZ

4 3 00008040 [email protected]@3ha

5 4 00001069 [email protected][email protected] @QAEHXZ

They represent class Cdll_object, constructors for classes, Funcindll functions, global variables G_ndll, and member functions of classes show. Here is the client code: DLL_OBJECT_CLIENT.CPP

#include "Dll_object.h"

#include <iostream.h>

Note the path, another way to load the DLL is Project | setting | In link settings

#pragma comment (lib, "Dll_object.lib")

int main (void)

{

cout<< "Call dll" <<endl;

cout<< "Call function in DLL" <<endl;

Funcindll ();//Just so we can call the function in the DLL.

cout<< "Global var in dll G_ndll =" <<g_nDll<<endl;

cout<< "Call member function of class Cdll_object in DLL" <<endl;

Cdll_object obj;

Obj.show ();

return 0;

}

Run this client to see:

Call DLL

Call function in DLL

Funcindll is called!

Global var in DLL G_ndll =9

Call member function of the class Cdll_object in DLL

ctor of Cdll_object

Function show in class Cdll_object

It is known that the client successfully accesses the global variable in the DLL and creates a C + + object defined in the DLL, and the member function of the object is also called.

A summary of the middle

Keep in mind that, in the final analysis, DLL is a C language dynamic link technology, in the output of C functions and variables is convenient, and in the output of C + + classes, functions need to pass various means, but also there is no perfect solution, unless the client is also C + +.

Remember, only COM is a technology that corresponds to the C + + language.

Let's start with a summary of each of these issues.

Explicit invocation and implicit invocation

When do I use an explicit call? When to use implicit invocation? I think that there is only one time when it is reasonable to use explicit invocation, when the client is not C + +. This is not an implicit invocation. For example, use VB to invoke C + + write DLLs. (VB I will not, so no example)

def and __declspec (dllexport)

The DEF function, in fact, is equivalent to the extern "C" __declspec (dllexport), so it can handle only C functions, not overloaded functions. and __declspec (dllexport) and __declspec (dllimport) can adapt to any situation, so __declspec (dllexport) is a more advanced method. Therefore, the prevailing view is not to use DEF files, and I agree with this view.

Calling DLLs from other languages

Calling DLLs from other programming languages, there are two biggest problems, the first is the problem of function symbols, which has been mentioned many times before. Here is a dilemma, if the use of extern "C", the function name remains the same, the call is more convenient, but does not support a series of C + + functions such as function overloading, if you do not use extern "C", it is very inconvenient to view the compiled symbol before calling.

The second problem is the problem of the function call stack order, namely __cdecl and __stdcall. __cdecl is a regular C/s + + calling convention, which is done by the caller when the function calls the stack after the call convention. __stdcall is the standard calling convention, which means that these functions will remove parameters from the stack before returning to the caller.

These two problems DLL can not be solved very well, can only be said to be used. But in COM, all of them have been solved perfectly. So, whether you want to implement language independence on the Windows platform or only use COM middleware.

In summary, DLLs are not easy to support C + + features such as function overloading, classes, unless the client also uses C + +. DLL's support for C functions is good, and I think that's one of the reasons why Windows libraries use C plus DLLs.

Writing DLLs in VC

It is very convenient to create, compile, link DLL in VC, click Fileànewàprojectàwin32 dynamic-link Library, enter the DLL name DLL_INVC and click OK. Then select the a DLL that export some symbols, click Finish. To get a complete DLL.

DLL files in enclosure: Http://www.blogjava.net/Files/wxb_nudt/DLL_SRC.rar

http://blog.csdn.net/chence19871/article/details/12611603

How to write a DLL (to deepen understanding with command-line compilation)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.