text: This series of articles discusses four common situations when working with DLLs:
  Visual Studio 6 makes it very easy to create C++ DLLs that contain functions or C++ classes.
  Open 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:
  Inside Test.cpp, you will see this code:
  // Test.cpp : Defines the entry point for the DLL application. // #include "stdafx.h" #include "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
  fnTest and
  CTest::CTest. If you compiled Test.dll at this point, you would have a DLL that could be called directly by other VC++ applications. The key mechanism that allows other VC++ applications to call Test.dll is contained 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 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
  #ifdef TEST_EXPORTS mean, and where is
  TEST_EXPORTS defined?
  What is happening is that, depending on whether
  TEST_EXPORTS is defined,
  TEST_API will be defined to export or import the DLL symbols. The export process results in the DLL's symbols being defined in Test.lib, which can be linked to any VC++ application that wants to access the DLL. When compiling Test.dll, I want to export, which means I want
  TEST_EXPORTS to be defined.
  Where is
  TEST_EXPORTS defined? This is one of the things I don't like about the DLL wizard - it puts the definition of
  TEST_EXPORTS on the command line. Go to Project | Settings | C/C++ | General, and you will see the Project Options:
  While this method certainly works, it is obscure, and may cause maintenance problems later on. I prefer to make explicit the definition of
  TEST_EXPORTS. I do this by deleting /D "TEST_EXPORTS" from the Project Options, and then changing Test.cpp to define
  TEST_EXPORTS:
  // 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
  #define TEST_EXPORTS is before the line
  #include "Test.h", so that the definition will be seen in the include file. Now Test.dll compiles just as before, and you will have a DLL that can be called from other VC++ applications.
  How 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
  OnInitDialog() method, 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 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 }
  This 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 . . .
  If 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:
  Note: This assumes that the DLL project and the EXE project have the same parent directory.
  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)
  Although 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:
  Now I get a clean compile. Before running the application, there is one more thing to do: copy Test.dll to the EXE's directory.
  I can then put a breakpoint in the
  OnInitDialog() method, and hit Go (F5):
  and I see that
  fnTest has returned 42 as expected. The
  CTest class can be tested in a similar way.
  #definepreprocessor definitions, one include file can be used by both the DLL and the application.
  In 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
  #pragma message, which appears in the output window of Visual Studio.
  The EXE1.exe demo tests the
  GetCpuSpeed() function and
  CDLL1::GetCpuSpeed() class method in DLL1.dll: