|
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThis series of articles discusses four common situations when working with DLLs:
Calling a DLL C++ function or class from a VC++ applicationVisual Studio 6 makes it very easy to create C++ DLLs that contain functions or C++ classes. Step 1Open Visual Studio and go to File | New:
Select Win32 Dynamic Link Library, enter a project name, and hit OK.
Select A DLL that exports some symbols and hit Finish. You will see the following files in the File View:
Step 2Inside Test.cpp, you will see this code: // Test.cpp : Defines the entry point for the DLL application. // #include "stdafx.h" #include "Test.h" BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } // This is an example of an exported variable TEST_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 definition CTest::CTest() { return; } Test.cpp contains code for // 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 should 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.dll class TEST_API CTest { public: CTest(void); // TODO: add your methods here. }; extern TEST_API int nTest; TEST_API int fnTest(void); What is going on here? What does What is happening is that, depending on whether Step 3Where is
While this method certainly works, it is obscure, and may cause maintenance problems later on. I prefer to make explicit the definition of // 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) { . . . Notice that the Step 4How can I call the functions in this DLL? To illustrate this, I create a demo app using Visual Studio. Select MFC AppWizard (exe), enter a project name, and click OK. Select Dialog based, then click Finish. Open the file XXXDlg.cpp, where XXX is the project name. Go to the .
.
.
// 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 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 5This code won't compile until I include the Test.h file from the DLL: // TestExeDlg.cpp : implementation file // #include "stdafx.h" #include "TestExe.h" #include "TestExeDlg.h" #include "Test.h" // <=== ADD THIS LINE . . . Step 6If you are doing a quickie demo, you will probably be tempted just to copy the DLL's Test.h into your exe project directory, so the compiler will be able to find it. For larger projects, this is not a good idea, because there is too much risk that you will update the DLL file, but forget to copy it into your exe directory. There is a simple way to solve this: go to Project | Settings | C/C++ | Settings | Preprocessor, and add to the Additional include directories field:
Now when I compile, I get ... 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 7Although I told the compiler about the DLL symbols, I also must tell the linker. Go to Project | Settings | Link and add the DLL's LIB file to the Object/library modules field:
Step 8Now I get a clean compile. Before running the application, there is one more thing to do: copy Test.dll to the EXE's directory. Step 9I can then put a breakpoint in the
and I see that Key Concepts
CommentsIn practice, I never use the approach described in Step 7. For large projects, the number of DLLs and LIB files quickly becomes unmanageable if handled in this way. What you want to do is set up project lib and bin directories, where you can keep copies of all current LIB, DLL, and EXE files. How do you tell the linker where to find these LIB files? There are two ways to do this in Visual Studio:
Which method is better? That depends on the situation in your development environment. Method 1 means all projects on a machine will share the same set of directory paths, but each developer's Visual Studio must be set up with those paths. Method 2 means that the paths can be customized for each project, and will be stored inside the project, so any developer can check out the project and begin development - provided the same paths exist on the developer's machine. It is common to establish standard drive+directory mappings, to eliminate endlessly tweaking the directory paths on each developer's machine. I still haven't discussed how to specify what LIB files to use. Here is my trick: In the DLL's Test.h, add two lines. The DLL1.h file now looks like: #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.dll
class TEST_API CTest
{
public:
CTest(void);
};
extern TEST_API int nTest;
TEST_API int fnTest(void);
This ensures that when a project includes the DLL's header file, it will also automatically link to the DLL's LIB file. The developer is notified of this by the DemoThe EXE1.exe demo tests the
Revision HistoryVersion 1.0 - 2004 February 29
UsageThis software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please to consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.
| |||||||||||||||||||||||||||||