Step by step: Calling C ++ DLLs from VC ++ and VB
Step by step, you can use VC and VB to call C ++ DLL.
Translated by Hans Dietrich
Introduction
This series of tutorials discusses four methods to use DLL in common cases.
Part 1
Call C ++ DLL functions from a VC ++ Application
Class For Calling C ++ DLL from a VC ++ Application
Part 2
Call C ++ DLL functions from a VB Application
Part 3
Class For Calling C ++ DLL from a VB Application
Part 4
Dynamic calling of C ++ DLL functions from VC ++ applications
Call C ++ DLL functions from a VC ++ Application
Visual Studio 6 makes it easy to create a dynamic connection library (DLL) that contains functions or classes.
Step 1
Open Visual Studio and select file | new menu item:
Select Win32 dynamic link library, enter the project name, and click OK.
Select a DLL that exports some symbols and click Finish. In File View, you will see the following project file:
Step 2
In test. cpp, you will see the following code:
// Test. CPP: defines the entry point for the DLL application. // # include "stdafx. H "# include" test. H "bool apientry dllmain (handle hmodule, DWORD plugin, lpvoid lpreserved) {Switch (listener) {case when: Case dll_thread_attach: Case dll_thread_detach: Case dll_process_detach: break;} return true ;} // This is an example of an exported variabletest_api int ntest = 0; // This is an example of an exported function. test_api int fntest (void) {return 42;} // This is the constructor of a class that has been exported. // see test. h for the class definitionctest: ctest () {return ;}
Test. CPP contains fntest and ctest: ctest. if you compile test. DLL, you will get a DLL that can be directly called by other VC ++ applications. what are the key mechanisms that allow other VC ++ programs to call? (Key mechanism) is included in test. h:
// The following ifdef block is the standard way of creating macros // which make exporting from a DLL simpler. all files within this DLL // are compiled with the test_exports symbol defined on the command line. // This symbol shocould not be defined on any project that uses this DLL. // This way any other project whose source files include this file See // test_api functions as being imported from a DLL, whereas this DLL // sees symbols defined with this macro as being exported. # ifdef test_exports # define test_api _ declspec (dllexport) # else # define test_api _ declspec (dllimport) # endif // This class is exported from the test. dllclass test_api ctest {public: ctest (void); // todo: add your methods here .}; extern test_api int ntest; test_api int fntest (void );
What happened here? # What does ifdef test_exports mean? Where is test_exports defined?
If test_exports is defined, test_api will be defined as _ declspec (dllexport) (DLL export). Otherwise, it will be defined as _ declspec (dllimport) (DLL import ). this will affect whether the ctest class defined later is the export class or the import class. this means that if we need to export, we need to define test_exports. when a VC ++ application wants to access this DLL. the LIB Link contains the DLL export symbol.
Step 3
Where is test_exports defined? DLL wizard does something I hate. It puts test_exports in the command line. Select project | Settings | C/C ++ | general, and you will see the project options:
Of course, this method is feasible. however, it is easy for people to worry about, and may cause maintenance problems. I prefer to clearly define test_exports: Remove/D "test_exports" from the project options, and then go to test. define it in CPP:
// Test. cpp: defines the entry point for the DLL application.
//
# Include "stdafx. H"
# Define test_exports // <= add this line
# Include "test. H"
Bool apientry dllmain (handle hmodule,
DWORD ul_reason_for_call,
Lpvoid lpreserved)
{
.
.
.
Note # define test_exports in # include "test. H "front. therefore, it is defined in the header file. now, we can recompile our test. DLL, we will get a DLL that can be called by other VC applications.
Step 4
How do we call functions in DLL? For example, I will use vs to create an example. select MFC Appwizard (exe), enter the project name, and click OK. select based dialog box. click Finish. open xxxdlg. CPP (XXX is your project name .) find the oninitdialog () member function and enter the following code:
... // Set the icon for this dialog. the framework does this automatically // when the application's main window is not a dialog seticon (m_hicon, true); // set big icon seticon (m_hicon, false ); // set small icon // code to test. DLL function: int n = fntest (); // <= add this line // code to test the ctest class: ctest test; // <=== add this line return true; // return true unless you set the focus to a control}
Step 5
We haven't finished writing the code yet. Now we need to include the DLL header file test. h:
// Testexedlg. CPP: implementation file // # include "stdafx. H "# include" testexe. H "# include" testexedlg. H "# include" test. H "// <= add this line...
Step 6
If you want to make a demonstration in time, you may try to copy the test. h to your project directory, then the compiler will find it. however, this is not a good idea when the project is large, because it may be dangerous when you update your DLL file. for example, you forgot to copy it to your EXE directory. here is a simple way to solve this problem: select project | Settings | C/C ++ | Settings | Preprocessor and add additional include directories: (directory of the DLL Project)
It is assumed that the DLL project and the EXE project have the same project directory.
Now, when I compile, I get a linker errors !!
Deleting intermediate files and output files for project 'testexe-Win32 debug '.
------------------ Configuration: testexe-Win32 debug --------------------
Compiling resources...
Compiling...
Stdafx. cpp
Compiling...
Testexe. cpp
Testexedlg. cpp
Generating code...
Linking...
Testexedlg. OBJ: Error lnk2001: unresolved external symbol "_ declspec (dllimport)
Public: _ thiscall ctest: ctest (void) "(_ imp _?? 0ctest @ Qae @ xz)
Testexedlg. OBJ: Error lnk2001: unresolved external symbol "_ declspec (dllimport)
Int _ cdecl fntest (void) "(_ imp _? Fntest @ yahxz)
Debug/testexe.exe: Fatal error lnk1120: 2 unresolved externals
Error executing link.exe.
Testexe.exe-3 error (s), 0 warning (s)
Step 7
Although I have already told the compiler the DLL symbol, the linker still does not know. so we must tell the linker .. select project | Settings | link to add the DLL lib file to the object/library modules:
----------------------------------------------
Step 8
Well, now the compilation is passed. Before we run the program, don't forget one thing: Copy test. DLL to the EXE directory.
Step 9
Next, you can place a breakpoint to the oninitdialog () function, and click Go (F5) to debug and run it:
We can see that fntest returns 42, which is the same as we predicted. The ctest class can also be tested using a similar method.
Key points.
The vs Project Wizard provides a good start for us to create VC ++ DLL.
Functions, classes, and variables can be exported from DLL.
Use the # define Preprocessor definition to define a header file that can be used by DLL and applications.
DLL exports its symbol, and the application imports this DLL symbol. when the application is compiled, the compiler uses the DLL symbol seen in the header file. When the application is linked, the linker passes the import to the database (test. lib) to see the DLL symbol.
When executing an application, the DLL must be placed in the same directory as the exe. dll can also be stored in the windows or system directory. This is also feasible, but it often causes some problems, so we should avoid using it like this.
Note:
In practice, I seldom use the method in Step 7. in this case, DLL and Lib files will become difficult to manage in large projects. we will think of creating a lib directory and a bin directory, where we will put all the Lib files, DLL files and exe files we want to use. if so, how can we tell the linker to find the Lib file? There are two ways to do this:
1. Select Tools | options | directories and set show directories for "library files". Add the path of the Lib file used by our project below.
2. Another method is to select project | Settings | link and set category to input. In the additional library path basket below, enter the path of the Lib file used by the project.
Which method is better? This depends on your development environment. The first method requires the directory paths to be shared by the entire project, and all vs requiring all developers must be set to these paths.
The second method allows each project to customize its own path and store it in the project. If the developer's computer stores the same directory, each developer can check out the project and design it ., this avoids setting the same path on each machine.
Up to now, I have not mentioned how to specify the Lib file to be used. My solution is to add two lines in test. h of DLL. Now it looks like below:
# Ifdef test_exports # define test_api _ declspec (dllexport) # else # define test_api _ declspec (dllimport) # pragma message ("automatic link to test. lib ") // <= add this line # pragma comment (Lib," test. lib ") // <= add this line # endif // This class is exported from the test. dllclass test_api ctest {public: ctest (void) ;}; extern test_api int ntest; test_api int fntest (void );
This ensures that when the project contains the DLL header file, it will automatically link the DLL lib to it, we can see from the output window of vs # The "automatic link to test" message sent by Pragma to us. lib "this message.