CS-360, DLLs Fall, 2000 R. Eckert DYNAMIC LINK LIBRARIES DLL--Basic building block of Windows; provide reusable code that can be shared by many applications simultaneously. Windows itself is a set of DLLs. A file containing ftns that can be called by other pgms or DLLs. Provides a way for a process to call a ftn that is not part of its executable code. Two methods of calling a ftn in a DLL: Load-time dynamic linking (early binding)--app makes an explicit call to a DLL ftn. DLL is automatically loaded (if not already there) when the program that needs it is loaded. In other words, the links to the DLL ftns are established as soon as the app and the DLL have been loaded into memory. This requires that the executable module of the app be bult by linking with the DLL's inport library, which supplies the info needed to locate the DLL ftn when the app starts. Run-time dynamic linking (late binding)--DLL can be loaded after execution of the app begins. The app calls LoadLibrary() to load the DLL when it is required and then GetProcAddress() to get the start address of the required DLL function. After the app no longer needs the DLL it should call FreeLibrary() to cause Windows to remove the DLL (if no other app is using it). File extension .DLL makes the DLL implicitly available to apps. (DLLs with other file extensions have to be loaded explicitly with LoadLibrary()--again, see printer notes.) DLL ftns are not directly executable--must be called. A DLL usually doesn't display a window or receive messages. A DLL Can be accessed by any number of apps; i.e., DLLs are a Windows solution to problem of sharing code (and data) between apps. DLL Code is loaded only once; any pgm using a ftn in an already-loaded DLL will be linked to the loaded DLL dynamically. This is very important in a multitasking system in which hundreds of apps may be trying to access some of the same functions. Provides for extending Windows--add new ftns in a DLL or write a new DLL. ADVANTAGES OF DYNAMIC LINKING-- Since many processes can use a DLL simultaneously, sharing a single copy in memory, memory space is saved and swapping is reduced. Ftns in a DLL can be changed without recompiling/relinking the apps that use them. (For statically-linked object code, the apps must be relinked when the ftns change.) This is especially useful for after- marketing modifications; e.g., a display driver DLL could be modified to support a new display. Pgms written in different programming languages can call the same DLL ftns, as long as pgms follow the functions' calling conventions. (Careful with stack conventions.) POTENTIAL DISADVANTAGES OF DYNAMIC LINKING-- The app is not self-contained. The DLL module must be present. Some commercial apps install DLLs in the Windows System directory without regard for the possibility that an old DLL with the same name is being overwritten. In the worst case, this could cause other apps that count on using ftns in the old DLL to not function properly. SOME DETAILS OF DLL OPERATION-- DLLs don't have their own stack--instead use the stack of the calling pgm. DLLs have their own local heap for static variables (e.g., strings). So all pgms accessing a DLL static variable can alter it. This can cause problems. If a DLL has to manage data for calling pgms, it should allocate a separate memory block for each calling pgm's data. It can use the caller's instance handle as an ID to keep track of which data block belongs to which pgm. But global blocks allocated by a DLL "belong" to the calling pgm. So any block blocks allocated by Pgm 1 will be freed when it is terminated, even if the DLL is still in memory to serve Pgm 2. (Usually a desireable behavior.) BUILD DETAILS-- The linker must be told that ftns located in a DLL are available for calling from another pgm or another DLL. One way to do this is to define an "EXPORT" macro and declare our DLL ftns as "EXPORT". For example, assume we have the following revstr() function defined in a DLL: BOOL CALLBACK revstr(HANDLE, int); In our DLL module's header file we could do the following: #define EXPORT extern __declspec(dllexport) /* define EXPORT macro */ EXPORT BOOL CALLBACK revstr(HANDLE, INT); /* declare the ftn as EXPORT */ This will tell the linker that revstr() is exportable. WRITING A DLL-- Most DLL's contain the ftn: DllMain()--like WinMain(): First ftn executed when DLL is loaded into memory by Windows Performs initialization stuff Runs only once (when DLL is loaded): int CALLBACK DllMain (HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved) Parameters passed to DLL from Windows: hInstance--DLL's instance handle. IF the DLL uses resources that require an instance handle (e.g., DialogBox), you should save hInstance as a global or static variable. Also if it calls ftns that require an instance handle [e.g., LoadString()], you will need to have access to hInstance. fdwReason--one of four values that indicate why Windows is calling DllMain(). pReserved--Reserved. The function could perform initialization code (e.g., initializing static data). DllMain() should return nonzero ==> Initialization was a success. (Value goes back to Windows.) [0==> initialization failed, and Windows will not run the program that calls the DLL.] For most simple cases, all that DllMain() has to do is: return(1); BUILDING THE DLL (with Mikcrosoft VC++ Developer Studio)-- 1. From Developer Studio, Use "File / New", and choose "Win32 Dynamic- Link Library" from the Projects tab. Give the DLL project a name, as usual. 2. Create or copy the source file(s) (.c or .cpp) that contain the DLL function definitions, the header file(s) (.h), and the resource script file (.rc), if present, to the directory containing the DLL project. Be sure that all DLL function declarations are "EXPORTed" in the .h file (see above). 3. Use "Project / Add to Project / Files" to add the source (.c or .cpp) files and the .rc file (if present) to the project. 4. Build the project. If the build is successful, the project's Debug directory will have the DLL (.DLL) file as well as the the import library (.LIB) file containing the information needed for dynamic linking. BUILDING AN APP THAT USES THE DLL (VC++)-- Now you can build an app that uses functions in the DLL. The process is as follows: 1. Use "File / New" as usual to create the "Win32 Application" as usual. 2. Create or copy the source (.c or .cpp), header (.h), and resource (.rc) files that constitute the app to the directory created for the app's project by Developer Studio. 3. Copy the .LIB file from the DLL's Debug directory (see above steps) into the app's directory created for the project by Developer Studio. 4. Use "Project / Add to Project / Files" to add the app's source (.c or .cpp) files and the .rc file (if present) to the project. Do the same to add the DLL's .LIB file to the project. 5. Build the project. This will produce the .EXE file in the app's Debug directory, as usual. 6. Copy the .DLL file from the DLL's Debug directory (see above steps) to the Debug directory of the app. (Remember that the DLL must be present.) 7. Run the .EXE file. COMPILING AND USING A DLL, AN EXAMPLE-- DLL: Source file: REVSTR.C -- provides ftn BlockRev() Header file: REVSTR.H -- declares and exports ftn BlockRev() App that uses the DLL: Source file: DLLCALL.C -- Calls the BlockRev() ftn Header file: DLLCALL.H -- Normal declarations Header file: REVSTR.H -- compiler must know about BlockRev() Resources: DLLCALL.RC Follow the procedures outlined above to build the DLL and the app. WHAT THE PROGRAM DOES-- The DLLCALL pgm uses the BlockRev() ftn in the DLL to reverse the order of the characters in a character string in response to user selecting "Show Strings" from the menu. DLLCALL's WndProc() gets a string from the resource file, allocates a global memory block, and calls BlockRev() in the REVSTR DLL, which reverses the characters in the string. Both the original and the reversed strings are displayed. WINDOWS AND IMPORT LIBRARIES-- Most of the Windows ftns we've been using reside in DLLs (e.g., GDI.EXE) We've used these ftns without having to import them or link their import library file (IMPORT.LIB) to our apps. Microsoft compiler/linker does this automatically.