16 Bit Windows (Win16) Programming Guidelines
This chapter describes how to use the compiler and linker to create Windows 3.x applications and DLLs. For the compiler, it explains how to choose memory models, and select the entry and exit code (if any) you want to generate for functions. For the linker, it describes switches and definition (.def) file statements for linking Windows objects.For information about:
- Creating Windows DLLs, see Building and Using Windows DLLs.
- Using the IDDE to create Windows applications and DLLs, see the User's Guide and Reference.
- Win32s and Win32 programming, see Win32 Programming Guidelines.
- Using the WINIO library, see the Run-Time Library Reference.
What's in This Chapter
- Choosing a memory model and prolog/ epilog code for a Windows program.
- Recompiling MFC 2.5 code for Digital Mars C++.
- dmc and CL options for Windows programming.
- Definition file directives for Win16 programs.
Compiling Windows Programs
This section describes how to choose memory models and use prolog and epilog code when compiling Windows programs with C++.Choosing a memory model
You can use these memory models in Windows programs:- Small
- Medium
- Compact
- Large
In general, if in doubt about what model to use, use the Large model. Specify the Large model with the -ml compiler option (see Compiling Code), or use the Memory Models subpage on the Build page of the IDDE's Project Settings dialog box. (The compiler will use the Small model unless you specify otherwise.)
Note: You cannot compile Windows 3.x programs with the Tiny, DOSX, Phar Lap, or NT memory models.
Windows applications usually consist of several source files. Always compile all the files in a Windows application with the same (preferably Large) memory model if possible, or explicitly declare a type for each pointer in a function prototype. If you are mixing near and far data references, make sure that all declarations match their corresponding definitions, or hard-to-find bugs can result.
For more information, see the section "Fine-Tuning with Mixed Model Programming" in Choosing a Memory Model.
Choosing entry and exit (prolog/epilog) code
C++ generates different types of entry code (the prolog) and exit code (the epilog) for functions in Windows applications. Different types of prolog/epilog code yield different instructions in the generated code and affect the size of the executable.You can choose the type of entry/ exit code you need from the Windows Prolog/ Epilog subpage on the Build page of the IDDE's Project Settings dialog box, or with the -W command line option. See "-W Compile for Microsoft Windows" in Compiling Code, for details about the -W option and modifiers.
When you select the No Prolog/ Epilog option (-W-or -W0), the compiler does not generate any prolog or epilog code.
Note: For compatibility with Microsoft C and C++, Digital Mars C++ supports the extension __declspec(naked), which tells the compiler not to generate prolog/ epilog code for individual functions. For information see Digital Mars C++ Language Implementation.
When you select the Full Prolog/ Epilog for all far Functions option (-W or -W1), the compiler generates one type of prolog and epilog for all far functions.
When you select the Reduced Prolog/ Epilog for non-exported far Functions option (-W2), two types of prolog and epilog result. All exported far functions have the prolog and epilog shown for the Full Prolog/ Epilog option.
When you select the Smart Callbacks -Load DS from SS for far Functions option (-W3), the compiler compiles far functions with a smart prolog and epilog that loads the data segment from the stack segment.
For more information on Windows entry and exit code, see Chapters 7 and 19 in Programming Windows 3.1 by Charles Petzold (3rd Ed.).
Warning: Only use the "Smart Callbacks" option with applications in which the data segment is the same as the stack segment (DS== SS). Do not use it with DLLs.
When you select the Windows Protected Mode Executable option (-WA), the compiler generates a protected mode Windows application with callback functions marked with __export.
When you select the Windows Protected Mode DLL option (-WD), the compiler generates a protected mode Windows DLL with callback and exported functions marked with __export.
Recommendations for using the prolog/ epilog options
The following combinations of options are recommended:- To get a program working, just use -W.
- For maximum program speed and compactness, use -WA or -WD.
- When you are using -WA or -WD and want to debug the resulting executable, use the m (generate INC BP / DEC BP to mark far stack frames) modifier also. This enables many debuggers (including the Digital Mars debuggers) to distinguish between near and far call frames.
- Use the -2 (optimize for 80286 CPU) compiler option in combination with -W1 when compiling applications or DLLs that will run in protected mode only. You can also consider using -3 or -4, provided you specify that the application will only run in enhanced mode.
- Don't use -W3 or -WA for code that will be part of a DLL.
- Don't use -Ws (smart callbacks) for code that will be part of a DLL.
- When using -W2, -WA, or -WD, make sure you mark all callback functions and exported functions as __export.
- Don't use -WA or -WD on code to be run in Windows real mode.
- If _loadds (or -mu) is not used for the function, DS will probably not have the correct value in it when the function is called.
- If _loadds (or -mu) is used, DS will have the correct value in it as long as there is only one instance of the program running. A second instance will have DS set to the value for the first instance.
Using the -Wb option
The -Wb option (assume DS != DGROUP) causes the compiler to issue a warning whenever a segment fixup is done to DGROUP. -Wb does not affect code generation; it only reports when a DGROUP segment fixup is generated. If your program is using an alternate data segment, a segment fixup to DGROUP could be a program bug.In Large model code, DGROUP fixup can be triggered by constructs like this:
static int x; static int *px = &x; /* DGROUP fixup */ static char *p = "abc"; /* DGROUP fixup */To eliminate the fixup, make the pointers near pointers:
static int __near *px = (int __near *)&x; static char __near *p = "abc";Using near pointers will result in DGROUP relative fixups, not DGROUP segment fixups. Alternatively, the pointers can be initialized at run time, as the compiler generates code that uses DS to initialize segment values.
Eliminating DGROUP segment fixups is useful for:
- Special purpose code, where the data segment needs to be relocatable.
- Embedded systems, where the data segment will be initialized from a ROM-based image.
- 16-bit Windows DLLs, where the DLL was made to be multi-instanced by creating multiple copies of DGROUP at run time.
Note: -Wb will cause the compiler to generate errors if you are using __loadds (because DS is reloaded from DGROUP), or if you are using -Wd (load DS from DGROUP) for Windows prologs.
Compatibility with Microsoft
The table below list Microsoft C Version 7 and Visual C++ options for generating prolog/ epilog code, and their Digital Mars C++ equivalents:
Microsoft | Digital Mars | Result |
---|---|---|
-Gw | -W | Full Windows prolog/ epilog for far functions. |
-Au | -mwu | Assume DS!= SS and load DS on entry to each function. |
-Aw | -mw | Assume DS!= SS. |
-GA | -WA | Optimized protected mode Windows application. |
-GD | -WD | Optimized protected mode Windows DLL. |
-GEa | -Wa | Load DS from AX. |
-GEd | -Wd | Load DS from DGROUP. |
-GEe | -We | Emit EXPDEF records for all exported functions. |
-GEf | -W-r | Create prolog/ epilog code for all far functions. |
-GEm | -Wm | Add inc BP/ dec BP to prolog/ epilog for far functions. |
-GEr | -W2V | Real mode, reduced prolog for non-exported functions. |
-GEs | -Ws | Load DS from SS. |
-Gq | -Wtxme | Reduced prolog/ epilog. Equivalent to -GW for MSC V6. (Digital Mars C/C++ generates a full prolog/ epilog for __far __export functions; MSC V6 does not.) |
-GW | -Wtxmev -D_WINDOWS | Reduced prolog/ epilog for real mode Windows functions. |
Recompiling MFC 2.5 Code for Digital Mars C++
When recompiling MFC 2.5 code for Digital Mars C++, use the compiler options listed below for the libraries linked with:
Use these options... | With these libraries... |
---|---|
-WA -ml | lafxcw |
-WA -ml -D_DEBUG | lafxcwd |
-WD-r -ml -D_USRDLL | lafxdw |
-WD-r -ml -D_DEBUG -D_USRDLL | lafxdwd |
-WA-r -ml -D_DEBUG -D_AFXDLL | smfc25d (for Windows application) |
-WD-r -ml -D_DEBUG -D_AFXDLL | smfc25d (for Windows DLL) |
-WA-r -ml -D_AFXDLL | smfc25 (for Windows application) |
-WD-r -ml -D_AFXDLL | smfc25 (for Windows DLL) |
-WA-r -ml -D_DEBUG -D_AFXDLL | smfco25d, mfco250d (for Windows application) |
-WD-r -ml -D_DEBUG -D_AFXDLL | smfco25d, mfco250d (for Windows DLL) |
-WA-r -ml -D_AFXDLL | smfco25 (for Windows application) |
-WD-r -ml -D_AFXDLL | smfco25 (for Windows DLL) |
-WA-r -ml -D_AFXDLL | smfcd25 (for Windows application) |
-WD-r -ml -D_ AFXDLL | smfcd25 (for Windows DLL) |
-WA-r -ml -D_ DEBUG -D_ AFXDLL | smfcd25d (for Windows application) |
-WD-r -ml -D_ DEBUG -D_ AFXDLL | smfcd25d (for Windows DLL) |
You might also need the following options:
- Use the -gf compiler option if you are linking with these libraries: smfc25d. lib and mfc250d. lib. In general, use -gf when using the DLL form of MFC.
- Use the -g compiler option if you are linking with these libraries: lafxcwd. lib and lafcdwd. lib.
For more information on the using the MFC 2.5 libraries, see the "read me" file SRC\MFC16\mfcsrc.txt in the distribution.
Win16 Definition File Directives
These are the directives used in definition files for Win16 programs. For descriptions of these directives see Definition File Directives.
NAME name[ WINDOWAPI| WINDOWCOMPAT| NOTWINDOWCOMPAT ] [ NEWFILES ] LIBRARY name [ INITGLOBAL | INITINSTANCE ] DESCRIPTION 'descriptive line' EXETYPE [WINDOWS 3.00 | WINDOWS 3.10 | DOS4 | DOS | UNKNOWN] HEAPSIZE [number | MAXVAL] NEWFILES PROTMODE REALMODE STACKSIZE number STUB 'filespec' CODE [PRELOAD | LOADONCALL] [EXECUTEONLY | EXECUTEREAD] [MOVEABLE | FIXED ] [IOPL | NOIOPL] [CONFORMING | NONCONFORMING] [DISCARDABLE | NONDISCARDABLE] [SHARED | NONSHARED] DATA [NONE | SINGLE | MULTIPLE] [PRELOAD | LOADONCALL] [MOVEABLE | FIXED ] [READONLY | READWRITE] [IOPL | NOIOPL] [SHARED | NONSHARED] SEGMENTS { name [CLASS 'class'] [PRELOAD | LOADONCALL] [EXECUTEONLY | EXECUTEREAD] [MOVEABLE | FIXED ] [READONLY | READWRITE] [DISCARDABLE | NONDISCARDABLE] [IOPL | NOIOPL] [NONCONFORMING | CONFORMING] [SHARED | NONSHARED] } IMPORTS { [internal=] externalfile.func } EXPORTS { extname [= intname] [@ number [RESIDENTNAME | NONAME]] [parms] [NODATA] }