CPGuide - Dynamic Linking: Difference between revisions
Created page with "By IBM '''Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation''' This content is part of the Control Progra..." |
No edit summary |
||
Line 34: | Line 34: | ||
The advantages and disadvantages of static linking are summarized in the following table. | The advantages and disadvantages of static linking are summarized in the following table. | ||
<PRE> | |||
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ | ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ | ||
³Advantages ³Disadvantages ³ | ³Advantages ³Disadvantages ³ | ||
Line 49: | Line 49: | ||
³ ³libraries). ³ | ³ ³libraries). ³ | ||
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ | ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ | ||
</PRE> | |||
Dynamic linking permits several applications to use a single copy of an executable module. The executable module is completely separate from the applications that use it. Several functions can be built into a DLL, and applications can use these functions as though they were part of the application's executable code. You can change the dynamically-linked functions without recompiling or relinking the application. | Dynamic linking permits several applications to use a single copy of an executable module. The executable module is completely separate from the applications that use it. Several functions can be built into a DLL, and applications can use these functions as though they were part of the application's executable code. You can change the dynamically-linked functions without recompiling or relinking the application. |
Revision as of 22:41, 1 September 2016
By IBM
Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation
This content is part of the Control Program Programming Guide and Reference.
Static versus dynamic linking
Most programmers are familiar with static linking; an application calls a routine or procedure whose code is not found in the application's source file. The routine is external to your source file and is declared as such. When the source file is compiled, the compiler places an external reference for the routine in the application's object (.OBJ) file. To create the executable file (.EXE) for the application, the application's object file is linked with an object file that contains the code for the routine. The result is an EXE file that contains the application code, as well as a copy of the code for the routine. The following figure illustrates the process of building a statically linked application.
My_Application.OBJ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ EXTERNAL ³ ³ Your_Routine ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ . ³ My_Application.EXE ³ . ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ CALL ???; Your_Routine ÃÄÄ¿ ³ . ³ ³ ³ ³ ÚÄÄÄÄÄÄ¿ ³ . ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÃÄ�³ LINK ÃÄÄÄ�³ CALL xxx ³ ³ ÀÄÄÄÄÄÄÙ ³ . ³ Your_Library.OBJ ³ ³ ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ xxx: ³ Your_Routine: ³ ³ PUBLIC ³ ³ ³ ³ ³ Your_Routine ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ ³ ³ ³ ³ Your_Routine: ³ ³ ³ ÃÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
Static Linking
When OS/2 loads a statically linked program, all the code and data are contained in a single EXE file and the system can load it into memory all at once.
The advantages and disadvantages of static linking are summarized in the following table.
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³Advantages ³Disadvantages ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Compile in pieces ³External routines built into ³ ³ ³EXE (making EXEs larger) ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³Can create libraries of ³EXE cannot be changed without ³ ³routines that can be linked ³relinking. ³ ³with applications. ³ ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ ³External routines cannot be ³ ³ ³shared (duplicate copies of ³ ³ ³libraries). ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
Dynamic linking permits several applications to use a single copy of an executable module. The executable module is completely separate from the applications that use it. Several functions can be built into a DLL, and applications can use these functions as though they were part of the application's executable code. You can change the dynamically-linked functions without recompiling or relinking the application.
The advantages of dynamic linking are:
Reduced memory requirements
Many applications can use a single DLL simultaneously. Since only one copy of the DLL is in memory, this saves memory space and, in general, the code is discardable.
Simplified application modification
An application can be enhanced or modified simply by changing a DLL. For example, if an application uses a DLL to support video output, several displays can be supported by different DLLs. The application can use the DLL that supports the appropriate display.
Flexible software support
DLLs can be used for after-market support. In the display-driver example, a new DLL can be provided to support a display that was not available when the application was shipped. Similarly, a database application can support a new data-file format with a new DLL.
Transparent migration of function
The DLL functions can be used by applications without any understanding of how the functions actually do their work. Future changes to the DLL are transparent to the application, as long as the input and output parameters remain the same.
Multiple programming language support
A function in a DLL can be used by an application written in any programming language, as long as the application understands the function's calling convention.
Application-controlled memory usage
Applications can make decisions about which DLL routines they want to load into memory and use. If a DLL is not used, it does not have to be loaded.
DLLs can be used to implement subroutine packages, subsystems, and interfaces to other processes. In OS/2:
Some DLLs are interfaces to the kernel.
The worker routines for the OS/2 API reside in the OS/2 kernel. Applications, which run at privilege level 3, usually can make direct calls to the kernel, which runs at privilege level 0. Some calls, however, must be linked through a DLL. For example, an application that calls DosOpen is linked to the DLL DOSCALL1.DDL. This library contains the definitions for some system functions. When a system function is called, OS/2 makes the call to the kernel on behalf of the application.
Some DLLs are interfaces to devices.
DLL subsystems can virtualize devices and manage the device for its clients.
Some DLLs provide an open system architecture for software.
Add-ons to OS/2 can be provided easily and by anyone.
OS/2 provides two varieties of dynamic linking: load-time and run-time. In load-time dynamic linking, references are resolved when an application is loaded. In run-time dynamic linking, references are resolved when the application runs.
Load-Time Dynamic Linking
Load-time dynamic linking is similar to static linking in that an application can call a routine that is not found in the application's source file. In load-time dynamic linking, however, an application is linked with a library file that contains a record that describes where the routine can be found instead of with a file that contains the code for the routine. The resulting application executable file contains this record and not a copy of the routine's code. The following figure illustrates the process of building a load-time dynamically linked application.
In the example in the following figure, the LIB file contains a record that describes where the code for a set of functions can be found. In this case, the code for the function Your_Routine can be found in the file, or module, Your_Routines.DLL under the entry point name Your_Routine. (The entry point name does not have to match the function name.) The function code also can be referenced by its ordinal value.
My_Application.OBJ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ EXTERNAL ³ ³ Your_Routine ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ . ³ My_Application.EXE ³ . ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ CALL ???; Your_Routine ÃÄÄ¿ ³ . ³ ³ ³ ³ ÚÄÄÄÄÄÄ¿ ³ . ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÃÄ�³ LINK ÃÄÄÄ�³ CALL ??? ³ ³ ÀÄÄÄÄÄÄÙ ³ . Ã�Ä¿ Your_Library.LIB ³ ³ ³ ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ ³ ³ ³ ³ Reference to ³ ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ ³ ³ ³ ³ function name: ³ ³ ³ Your_Routine.1 ³ ³ ³ Your_Routine ³ ³ ³ Your_Routine. ÃÄÄÙ ³ ³ ³ ³ Your_Routine ³ ³ module name: ³ ³ ³ ³ ³ Your_Routines ÃÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ ³ ³ entry point ³ ³ ordinal value: 1 ³ ³ entry point name ³ ³ Your_Routine ³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
Dynamic Linking
When the application is loaded, the system resolves the dynamic-link references, as shown in the following figure.
My_Application.EXE Your_Routine.DLL ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ . ³ ³ Entry Table ³ ³ . ³ ³ 1 ÄÄÄÄÄÅÄÄ¿ ³ . ³ ³.................³ ³ ³ Call ??? ³�Ä¿ ³ ³ ³ ³ . ³ ³ ³ ³ ³ ³ ³ ³ yyy: ³ Your_Routine: ³�ÄÙ ³ ³ ³ ³ ³ ³.................³ ³ ³ ³ ³Reference to ³ ³ ³ ³ ³ ³ ³ ³ ³ ³Your_Routine.1 ÃÄÄÙ ÀÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÙ ³Your_Routine. ³ ³ ³ Your Routine³ ³ ÀÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÙ ³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ ÚÄÄÁÄÄ¿ ³LOAD ³ ÀÄÄÂÄÄÙ ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ³ My_Application code Other code ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Call yyy ÄÄÄÄÅÄÄÄÄÄ� yyy: ³ Your_Routine ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
Resolving Dynamic Link References
If a program contains dynamically linked references, the system must process the information in the references. If the DLL already is in memory, the system adds information to the executable code so that the code can use the DLL functions. If the required DLLs are not already in memory, OS/2 searches the path specified by the LIBPATH environment variable. If the system cannot find the DLLs, it stops loading the application and reports the error. If the system finds the DLLs, it loads them. When DLLs are loaded into memory, OS/2 notifies the application where the DLL functions can be found.
When a DLL is loaded into memory is determined by how the DLL was built. A DLL is built like an application, using a module-definition (.DEF) file. The CODE statement in a DEF file describes the attributes of application or DLL code. The load option for the CODE statement determines when application or DLL code is loaded:
PRELOAD
Code is loaded as soon as a process accesses the DLL. This leads to increased performance (in terms of accessing the DLL functions) while decreasing available memory. This option might be preferable if only one program is running.
LOADONCALL
Code is loaded when needed. This is the recommended choice and the default.
Run-Time Dynamic Linking
When an application contains a reference to a DLL, the DLL is loaded into memory when the application is loaded (unless the DLL already is in memory). If the application uses functions in several DLLs, all of those DLLs are loaded into memory. Some applications might use functions in several DLLs but use only a few of them at any one time. For example, an application that supports many different printers or plotters might use functions in many DLLs but need only one of them at a time, depending on the printer or plotter the application is supporting. If the user switches to a different printer or plotter, another DLL will be used, but the first will remain in memory. Loading DLLs this way can be very wasteful.
To avoid this problem, applications can take advantage of run-time dynamic linking and load and unload DLLs as they are required. The process of building a run-time dynamically linked application is similar to the process of building a load-time dynamically linked application. However, the EXE file for a run-time dynamically linked application does not contain a record describing where the external routines can be found. Instead, the application explicitly tells OS/2 when to load and free the dynamic link module.
Applications load and unload DLLs and call functions whose code resides in those DLLs by:
- Calling DosLoadModule to get a handle to the DLL module.
- This function also loads the DLL into memory or attaches to it, if it already is loaded.
- Calling DosQueryProcAddr to get a pointer to a function within the DLL.
- Calling the function indirectly through the pointer returned by DosQueryProcAddr.
- Calling DosFreeModule to free the handle to the DLL module.
- When all handles to the DLL module have been freed, the DLL is unloaded from memory.
An application also can request information about a DLL from the system. The application can use the DosQueryModuleHandle function to determine whether a DLL has been loaded into memory already. The DosQueryModuleName function returns full path information for the DLL file.
Following are the advantages of run-time dynamic linking:
Memory is consumed as needed.
DLLs can be loaded and unloaded as they are used. Unused DLLs do not have to be loaded into memory, and memory can be released when the application has finished using the DLL.
Applications can recover from DLL NOT FOUND.
Applications can make decisions. If a load-time DLL cannot be found, the application terminates immediately. If a run-time DLL cannot be found, the application receives an error value from the DosLoadModule function, and it can use another DLL or specify a full path for the DLL. If a function is not available, the DosQueryProcAddr function returns an error value, and the application can take appropriate action.
DLL and function names can be variable.
An application programmer does not have to know the names of the DLLs the application will be using or the names of the functions within the DLL. The application can read the names of the DLL or the functions from a configuration file or obtain the names from user-supplied input.
DLLs can be anywhere.
The application can specify the full path for the DLL. Load-time DLLs must be in a directory in the path specified by the LIBPATH environment variable, but run-time DLLs can be in other directories.
Dynamic link library (DLL) data
Most DLLs will contain some data. Whereas DLL code is shared by all processes that use it, DLL data can be shared or not shared by all processes that use it. Data that is specific to each process that uses the DLL (that is, to each instance of the DLL) is called instance data. Data that is shared by all processes that use the DLL is called shared or global data.
The first time a process references a DLL (and it is loaded or its usage count is incremented because it is already loaded), a separate copy of the DLL's instance data is created. Modifications to the instance data for one process do not affect the instance data for any other process. The system, however, maintains only one copy of a DLL's shared data for all processes that use the DLL. Every process that uses the DLL can access the same data. If one process modifies the data (increments a count, for example), the data will be modified for all processes that use the DLL.
Because changes to shared DLL data by one process are visible to the DLL code when called by another process, shared data provides DLLs with a mechanism for tracking processes that use it. This is crucial to subsystems, which are DLLs that manage resources (for example, devices, queues, and so forth).
There usually is only one data and one code object, or segment, defined in a C source file. This means that a DLL that has instance and shared data is built from more than one C source file, with a default automatic data segment and with named data segments. How data is defined is determined by how the DLL is built. A DLL is built like an application, using a DEF file. The DATA statement in a DEF file describes the attributes of application or DLL data. Following is a list of the available options for the DATA statement:
Options
Result
MULTIPLE
OS/2 creates a unique copy of the automatic data segment for each process that uses the DLL. Modifications made by one process do not affect any other process. This is the default.
SINGLE
OS/2 creates only one automatic data segment for all processes that use the DLL. If one process modifies the data, the data will be modified for all processes that use the DLL.
READWRITE
Enables you to read from or write to the automatic data segment. This is the default.
READONLY
Enables you to read only from the automatic data segment.
LOADONCALL
The automatic data segment is loaded into memory as needed. This is the default.
PRELOAD
The automatic data segment is loaded as soon as a process accesses a DLL.
ÚÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄ¿³ Process A ³ ³ Process B ³ ÀÄÄÂÄÄÄÄÄÂÄÄÙ ÀÄÄÂÄÄÄÄÄÂÄÄÙ ³ ³ ³ ³ ³ ³ ³ ³ ÚÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÅÄÄ¿ ³ ³ ³ Dynamic Link ³ ³ ³ ³ ³ ³ Functions ³ ³ ³ ÀÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÅÄÄÙ ³ ³ ³ ³ ³ ³ ÚÄÄÄÄÄÄÄ¿ ³ ³ ³ ³ ³Shared ³ ³ ³ ³ ÀÄÄÄÄÄ�³ Data ³�ÄÄÄÄÄÄÄÄÄÙ ³ ³ ÀÄÄÄÄÄÄÄÙ ³ ³ ÚÄÄÄÄÄÄÄÄÄ¿ ³ ³ ³Process B³ ³ ³ ÚÄÁÄÄÄÄÄÄÄ¿ ³�ÄÄÄÄÄÄÄÄÄÄÄÙ ³ ³Process A³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄ�³Instance ÃÄÙ ³ Data ³ ÀÄÄÄÄÄÄÄÄÄÙ
DLL Data
You also can create a DLL with both shared and instance data. For more information, see Using Shared and Instance Data
DLL initialization and termination
A DLL is initialized and terminated by the default _DLL_InitTerm function. When a process gains access to the DLL, this function initializes the necessary environment for the DLL, including storage, semaphores, and variables. When the process frees its access to the DLL, the _DLL_InitTerm function terminates the DLL environment created for that process. The _DLL_InitTerm function is called automatically when you link to the DLL.
The _DLL_InitTerm function can be executed once when the DLL is first loaded into memory, or it can be executed each time a new process first accesses the DLL. The LIBRARY statement in the module-definition file is used to specify when the _DLL_InitTerm function is to be executed. Following is a list of of the available options for the LIBRARY statement.
Options
Result
INITINSTANCE
The _DLL_InitTerm function is called the first time the DLL is loaded for each process that accesses the DLL.
INITGLOBAL
The _DLL_InitTerm function is called only the very first time the DLL is loaded. This is the default.
TERMINSTANCE
The _DLL_InitTerm function is called the last time the DLL is freed for each process that accesses the DLL. As an example, the following statement, identifies the executable file as a DLL and specifies that SAMPLE03 is the name of the DLL:
LIBRARY SAMPLE03 INITINSTANCE TERMINSTANCE
It also specifies that the _DLL_InitTerm function is to be called the first time the DLL is loaded for each process that calls the DLL and the last time the DLL is freed for each process that calls the DLL.
When OS/2 starts executing a DLL, it sets the CPU registers to known values, but only for 16-bit DLLs. All 32-bit DLLs are called with a stack frame, like all other API calls.
Initialization/termination functions can be written in a high-level language. For more information on writing your own initialization/termination function, see Creating an Initialization/Termination Function.
Building a DLL
Building a DLL is not very different from building a conventional static library. The following sections show how you can use OS/2 tools and functions to create, manage, and use DLLs.
Use of protected memory by DLLs
External Function References
When you compile or assemble an application, the compiler or assembler generates an object module for the code in the application. If you use any functions that are external to your application (their code is in another object module), the compiler or assembler adds an external function reference to your application's object module.
The Linker resolves these external references. If the Linker finds the external function in a library called an import library or in an IMPORTS statement in the application's module-definition file, the code for the external function is in a DLL. To resolve external references to DLLs, the Linker simply adds information to the executable file that tells the loader where to find the DLL code when the application is loaded.
Module-Definition Files
The module-definition file is an important tool for building DLLs. This file contains information that tells OS/2 the name of the DLL, when to load the DLL, how to manage memory for the DLL, and when to initialize the DLL.
When you create a DLL, the module-definition file must contain a list of all the functions in the DLL that can be called by an application (or by another DLL). You specify these external functions by using an EXPORTS statement in the module-definition file.
You also must tell the Linker where to find the external functions in your application. If the functions are in a DLL, you can use an IMPORTS statement in the module-definition file for the application to tell the Linker where to find the DLL functions. You also can use an import library to tell the Linker where to find your DLL functions.
Import Libraries
A conventional library contains object modules for a number of functions. The library is a convenient way to manage a large number of modules and use them in your executable code by linking to the library. The Linker uses the external references in your object module to determine which modules must be pulled out of the library.
An import library does not contain any object modules. Instead, the import library contains information that tells the Linker what DLLs are used by your application and the location of the functions your application uses within each DLL.
Like a conventional library, an import library primarily is a convenience. Instead of specifying all the functions your application imports in its module-definition file, you can link with the import library and let the Linker resolve the external references in your object module.
You use import libraries every time you compile and link a program that uses the OS/2 API. All the OS/2 functions are implemented in DLLs, and OS2386.LIB is an import library that tells the Linker where to find each OS/2 function.
For more information about module-definition files and import libraries, see the online Tools Reference in the OS/2 Warp Developer's Toolkit.
Creating a Simple DLL
DLLs typically are used to provide common functions that can be used by a number of applications. The following figure shows a C source file, MYPUTS.C, for a DLL that contains a simple string-printing function.
#include <os2.h> #define HF_STDOUT 1 /* standard-output handle */ VOID EXPENTRY myPuts(PSZ pszMsg) { ULONG ulWritten; while(*pszMsg) { DosWrite(HF_STDOUT, pszMsg, 1, &ulWritten); pszMsg++; } }
The following figure shows the module-definition file, MYPUTS.DEF, for this DLL.
LIBRARY myputs DATA SINGLE SHARED EXPORTS myPuts
The LIBRARY statement names the DLL (MYPUTS.DLL). The DATA statement tells the system that this DLL will share all data with each process that uses the DLL. The EXPORTS statement indicates that the function myPuts can be used by applications and DLLs.
The DLL is compiled and linked like any application. You can use IBM's ICC and LINK386, as shown below, to create MYPUTS.DLL.
icc /C+ /Ge- myputs.c
link386 /noi myputs, , nul, OS2386, myputs
When the DLL has been created, you must copy it to one of the directories indicated by the LIBPATH environment variable in your CONFIG.SYS file.