HOWTO: Export Functions from a 16-bit DLL

Last reviewed: August 26, 1997
Article ID: Q148832
The information in this article applies to:
  • Microsoft Visual C++ for Windows, versions 1.0, 1.5, 1.51, 1.52

SUMMARY

This article describes how to export functions from a 16-bit Dynamic Link Library (DLL). The topics discussed include:

  • How the memory model affects function declarations.
  • Segment setup - what does SS!=DS mean?
  • Function Prolog and Epilog options.
  • How to declare functions as exported -- through function declaration or module-definition file (.def file)?
  • The C-run-time libraries for DLLs.

Sample code is also included along with the associated compiler and linker options.

If you are exporting callback functions, please see the following article in the Microsoft Knowledge Base:

   ARTICLE ID: Q100659
   TITLE     : Exporting Callback Functions Not Required in
               Win32-Based Apps

MORE INFORMATION

How the Memory Model Affects Function Declarations

The code and data for the DLL will reside in segments that are separate from the segments that hold the code and data for the .exe file. The .exe file and the DLL share the stack of the .exe file, so you do not need to do anything special to pass data as arguments from the .exe file to the DLL. Because the calling function (in the .exe file) resides in a segment separate from the called function (DLL), the called function must be declared as FAR (or __far). Because the data of the calling function resides in a segment separate from the called function, pointer arguments and pointer return values in the declaration of the called function must also be declared as FAR. The FAR declaration can be explicit (specified by the function declaration) or implicit (specified by the memory model).

To declare a function as FAR explicitly, precede the function name with FAR or __far, as in this example:

   int FAR Function ();

To declare a pointer as FAR explicity, precede the asterisk with FAR or __far, as in this example:

   int FAR *pInt;

Segment Setup - What Does SS!=DS Mean?

DLL functions use the stack of the calling function, so the DLL's segment addresses for the stack segment and for the data segment are not the same. The Visual C++ compiler can make assumptions about the value of these segment registers. Therefore, when creating a DLL, you must inform the compiler not to make assumptions of this nature.

There are two options that the Visual C++ compiler recognizes concerning SS!=DS. The option used for DLLs is SS!=DS, which means that the DS is not loaded on function entry. The DS register will be loaded in the Prolog code for the function. On the command-line options for the compiler, this option is specified by /Axw where the x is either S, M, or L (small, medium, or large memory model).

Function Prolog and Epilog Options

If any function in the DLL is declared with __export, the DLL should be compiled with the compiler option to generate prolog/epilog code for Protected Mode DLL Functions (/GD). This option will only generate the prolog/epilog code for functions declared with __export. To generate the same prolog/epilog code for functions declared FAR (explicitly or implicitly), select the compiler option to "Generate for __far Functions" (/GEf). This will not export the functions, however. You will need to specify the exported functions in the EXPORTS section of the module- definition file (.def file).

How to Declare Functions as Exported

You have two options to export a function in a DLL, either use __export in the function declaration, or make an entry for the function in the EXPORTS section of the .def file. Using __export is straight-forward, but you will have much more control over how exactly the function is exported if you choose to use the .def file. These two options are not exclusive. For more information about these options, please see the following article in the Microsoft Knowledge Base:

   ARTICLE ID: Q77986
   TITLE     : Using _export Keyword or DEF File EXPORTS Statement

To use __export in the function declaration, precede the function name with the __export keyword, as in this example:

   int FAR _export Function ();

If you choose to use the .def file option, you must know the decorated function name (function names are decorated both in C and C++ by the compiler). You will find the decorated name in the .map file generated by the linker. This means that if you don't know the decorated name, you must first link the DLL without exported functions. Then find the decorated names in the .map file, construct the EXPORTS section of the .def file, and link the DLL again. C functions are decorated with a leading underscore. C++ functions have a more complex decoration.

For a more detailed discussion of the EXPORTS section of the .def file, please see the Visual C++ documentation.

The C-Run-Time libraries for DLLs

The C-run-time libraries for DLLs are XdllcYw.lib. Where X is the memory model and Y is the math library option. For example, Ldllcew.lib is the C-run-time library for large-memory-model DLLs using the standard math library. Do not use XlibcYw.lib; these are the C-run-time libraries for Windows-based applications, not for DLLs. For more information about library-naming conventions, please see the following article in the Microsoft Knowledge Base:

   ARTICLE ID: Q28173
   TITLE     : C Run-time Library History and Naming Conventions

Step-by-Step Example to Build the DLL

To build the DLL from the IDE:

  1. On the Project menu, click New, and select Windows dynamic-link library.

  2. Name it Mydll.

  3. Add the following Mydll.c file to the project. If you now build the project, the IDE will create the .def file (Mydll.def) shown in the "Mydll.def Sample Code" section of this article. Then it will use this .def file to build the DLL.

       // MyDLL.c
    
       #include "windows.h"
    
       // This is a FAR function that returns a FAR pointer to an integer.
       // It takes as arguments a FAR pointer to an integer and
       // a FAR pointer to a character array.
       // Note that this function is exported.
       // Note also that because you're compiling with a large memory model,
       // all occurrences of FAR may be omitted here.
    
       int FAR * FAR __export DLLfunc1 (int FAR *Marker, LPSTR Message)
       {
          char Title[200];
          wsprintf(Title,"Marker Number %d",*Marker);
          MessageBox(NULL,Message,Title,MB_OK);
          return Marker;
       }
    
    
To build the DLL from the command line:

  1. Hand code the Mydll.def file shown in the "Mydll.def Sample Code" section of this article.

  2. Use the following commands in sequence:

    Compile with:

          CL /ALw /GD /c MyDLL.c
    

    Link with:

          LINK /NOD /NOE MyDLL.obj,MyDLL.DLL,,LDLLCEW LIBW,MyDLL.DEF
    

    Create Import Library:

          IMPLIB MyDLL.LIB MyDLL.DLL
    

Mydll.def Sample Code

   ; MyDLL.DEF
   LIBRARY   MYDLL
   EXETYPE   WINDOWS
   CODE      PRELOAD MOVEABLE DISCARDABLE
   DATA      PRELOAD MOVEABLE SINGLE
   HEAPSIZE  1024
   EXPORTS
             WEP PRIVATE
   ; To implement your own Windows Exit Procedure add the following
   ; function to your application (referring to it in the .def file isn't
   ; necessary). The extern "C" is only required if module is C++.
   ; extern "C" int FAR PASCAL _WEP(int)
   ; {
   ;       /* Your WEP functionality goes here */
   ;  return 1;
   ; }

Step-by-Step Example to Build the Application

To build the application from the IDE:

  1. On the Project menu, click New, and select Windows application (.EXE).

  2. Add the following Myapp.c file to the project. Again, the IDE will create the Myapp.def shown in the "Myapp.def Sample Code" section of this article.

       // MyApp.c
    
       #include "windows.h"
    
       // This is a FAR function that returns a FAR pointer to an integer.
       // It takes as arguments a FAR pointer to an integer and
       // a FAR pointer to a character array.
       // Note also that since we are compiling with a medium memory model,
       // all occurrences of FAR below are required.
       // Note that the NEAR pointers to MainMarker and MainMessage are
       // converted to FAR in the function call to DLLfunc1 by the compiler.
    
       int FAR * FAR DLLfunc1 (int FAR *Marker, LPSTR Message);
    
       int FAR PASCAL WinMain (
          HINSTANCE hInstCurrent,
          HINSTANCE hinstPrevious,
          LPSTR lpszCmdLine,
          int nCmdShow)
       {
          int MainMarker = 123;
          char *MainMessage = "My WinMain message";
          DLLfunc1(&MainMarker,MainMessage);
          return 0;
       }
    
    

  3. On the Options menu, click Project, and in the Linker input Libraries setting, add Mydll.lib, and build the application.

To build the application from command line:

  1. Hand code the Myapp.def file shown in the "Myapp.def Sample Code" section of this article.

  2. Use the following commands in sequence:

    Compile with:

          CL /AM /GA /c MyApp.c
    

    Link with:

          LINK /NOD /NOE /STACK:8096 MyApp.obj,MyApp.exe,,LLIBCEW LIBW MyDLL,
          MyApp.DEF
    

Myapp.def Sample Code

   ; MyApp.DEF
   NAME      MYAPP
   EXETYPE   WINDOWS
   CODE      PRELOAD MOVEABLE DISCARDABLE
   DATA      PRELOAD MOVEABLE MULTIPLE
   HEAPSIZE  1024
   ;STACKSIZE 8096 ;Uncomment this line if /STACK:8096 is not used in
                   ;command line build for LINK. IDE build sets this option
                   ; by default

   EXPORTS
   ;    ===List your explicitly exported functions here===

Modified Version of Mydll.def File

If you do not use the __export keyword for the exported function, you need to modify the Mydll.def file where the decorated name of the function is added in the EXPORTS section as shown here:

   ; MyDLL.DEF
   LIBRARY   MYDLL
   EXETYPE   WINDOWS
   CODE      PRELOAD MOVEABLE DISCARDABLE
   DATA      PRELOAD MOVEABLE SINGLE
   HEAPSIZE  1024
   EXPORTS
             _DLLFunc1
             WEP PRIVATE
   ; To implement your own Windows Exit Procedure add the following
   ; function to your application (referring to it in the .def file is
   ; not required.)  The extern "C" is only required if module is C++.
   ; extern "C" int FAR PASCAL _WEP(int)
   ; {
   ;       /* Your WEP functionality goes here */
   ;  return 1;
   ; }

When building the DLL from the IDE, select "Generate for __far Functions" in the compiler's "Windows Prolog/Epilog" category.

When building the DLL from the command line, add the /GEf option as shown here:

   CL /ALw /GD /GEf /c MyDLL.c


Additional query words: 1.52a 1.52b 1.52c
Keywords : kb16bitonly VCGenIss
Version : WINDOWS:1.0,1.5,1.51,1.52
Platform : WINDOWS
Issue type : kbhowto
Solution Type : kbcode


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: August 26, 1997
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.