12.8 32-bit files
------------------
12.8.1 32-bit code under DOS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To compile a 32-bit DOS program use the command-line switch /d32. You
will to use a DOS extender as well, such as DOS4GW or ZRDX. The line
stub=path_name_stub_file in c--.ini indicates the path to and name of the
stub, for example:
stub=c:\c--\zrdx.exe
Without this line, the compiler will generate a 32-bit EXE file
without a DOS extender. If you use /ns instead of /d32, the line with
stub in c--.ini is invalidated and the EXE file again has no DOS
extender.
For a 32-bit DOS file you can use the compiler directives
?parsecommandline TRUE/FALSE or the extended variant ?argc TRUE/FALSE.
There is also support for ?atexit TRUE/FALSE.
The LE format is now used for 32-bit DOS files. Since this is a
standard format almost any stub which supports this format can be used.
LE files can be compressed with programs like upx.exe.
If you use a stub which then loads DOS4GW, there should be a special
signature at the beginning of the program. The compiler automatically
generates it on encountering the command-line switch /DOS4GW. You must
use this switch if you use 4GS as a stub.
There is also support for a block of code which uses a DPMI service
for switching and operating in 32-bit mode. The source text of this block
is foiund in startup.h-- and is compiled if the option /stub=dpmi is
found on the command line or if stub=dpmi is in c--.ini. A drawback of
this method of switching and operating in 32-bit mode is that the DMPI
service must be part of the boot sequence. Since the program is loaded as
an ordinary DOS program and only during operation does it switch to
32-bit mode, the program size is limited by amount of free DOS memory. An
advantage is the small size of the executable.
12.8.2 32-bit code under Windows
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To compile a Windows program use the command-line switch /w32.
If API functions are use in a program they must first be declared, as
follows:
extern WINAPI "DLL_name"
{
returncode procname1();
returncode procname2();
procname3();
}
where:
DLL_name is the file name and extension of the DLL library where the
functions are located
returncode is the type of return from the API functions, by default dword.
Windows programs have one feature that should not be overlooked - all
the parameters are transferred to the stack functions in inverse order
(so-called C style), but the stack is freed of parameters in these
functions themselves. This gives a hybrid of C and pascal function -
stdcall.
The switch /w32c is used to create Windows console programs.
Using the command-line option /j0 or the directive '#jumptomain NONE'
causes the program to be compiled without using the startup code
described in startup.h--.
Startup code for Windows programs is of the following format:
hThisInst=GetModuleHandleA(0);
#ifdef __CONSOLE__
hStdOut=GetStdHandle(-11);
#endif
lpszArgs=GetCommandLineA();
#ifdef __environ;
environ=GetEnvironmentStringsA();
#endif
main();
ExitProcess(EAX);
The global variables hThisInst therefore contain the handle of the
file being run, and lpszArgs contains the address of the file's command
line. Using the command-line options /p or /argc, or the directive
'#parsecommandline TRUE' or 'argc TRUE' in the beginning of the file
causes the compiler to create additional code which splits the command
line into parts. When compiling a console program, there is one
additional global variable hStdOut, which keeps the handl of the standard
output (to screen). If the option /env was specified during compilation,
the address for accessing the program's environment is stored in the
global variable 'environ'.
After 'main' is completed, 'ExitProcess' is executed, and it is
transfered to register EAX as a parameter. In order to terminate a
program, it is sufficient to exit 'main' after first loading the needed
return code into EAX.
Some compilers create DLL's in which the names of the exported
functions are of the format:
ProcName@8
The string after @ indicates the stack size with the parameters
transferred to the function.
Such functions are declared as follows:
extern WINAPI "name.dll"
{
ProcName@8 ;
}
i. e., without parentheses. When such a function is used in a
program, the name must be written without the suffix @8 -
ProcName(param1,param2);
12.8.3 Call API functions by ordinal number
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In dynamic linked libraries (DLLs) each function has in addition to a
name a unique ordinal number. Therefore API functions can be called not
only by name but by ordinal number. In theory the file will be loaded
faster when called by number. Since the output file does not contain
lists of function names which are called by numbers, the output file will
be slightly smaller.
To compile a file which will call API functions by number:
1. Give the compiler permission using the command-line switch /wo or
wo in c--.ini
2. Inform the compiler of the ordinal number for each function name.
Functions to which no number was assigned will be called by name. There
are two ways to set up a correspondence between function names and
numbers:
a) automatically using 'ind=name.dll' on the command line, which causes
the compiler to scan this library and import all function names and
numbers. (You can only import from PE format libraries).
b) manually list the API function and its number in the declaration, by
placing a period after the function name and then its number. Here is an
example of how to declare an APR function with its number:
extern WINAPI "user32.dll"
{
............
long MessageBoxA.429();
............
}
DLL libraries sometimes contain functions with numbers but no names.
You cannot call them by name, but you can do so by number (if you know
what the function is and does, of course). When declaring the API
function you have to think of a unique name for it and indicate the
actual number. Then you can refer to it by the name that you have made
up. If you accidentally compile this file without the switch '/wo' when
running the program you will be told that there is no such name in the
library.
Unfortunately there is no guarantee that the number of a particular
function will not differ in different versions of dynamic libraries, so
be careful when calling the function by its number.
12.8.4 Creating DLL's under Windows
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Dynamically linked libraries can be used to obtain smaller programs
and to speed up compilation. Drawbacks of their use include the need for
the DLL files to be present on the computer and the fact that the
programs take a little longer to load.
In order to make a function accessible to other programs, the source
code must contain before the function name the keyword _export, for
example:
void _export testproc()
{
....
}
To create a DLL, you must write a file containing functions with the
keyword _export. Auxiliary functions which may be needed by the exported
functions to work, need not be declared as _export. Therefore this file
must be compiled with the switch /dll, which gives a complete dynamically
linked library.
12.8.5 Initializing DLLs while loading
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sometimes when working with DLL functions you need to initialize some
variable with values which depend on the current state of the operating
system, for example to obtain the descriptor of this library.
In all other cases control a stub code is generated and control is
not passed to main. In fact main is not needed in this case.
When creating DLL files, main should look somewhat different than in
other cases:
dword main ( dword hInstDLL, reason, reserv )
{
...
}
12.8.6 Compiling resources
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The resource compiler built into C-- is not as capable as specialized
resource compilers but should be sufficient for most uses.
It is simplest to list what this built-in compiler cannot do. The
resource operators VERSION are not processed, nor are resources defined
by the user. If needed, data entered using these operators can be entered
using RCDATA instead. Many resource operators require the parameters
'loading' and 'memory'. There is no support here for these parameters,
which the compiler will simply skip over.
There are two ways to force C-- to process resources:
1. Include in the directive an '#include' with extension '.rc'. Such
files are considered as resource files. A resource file must be included
in a project only after including Windows headers files.
2. Resources can be placed anywhere in the body of the source code.
The code of the resources should start with the '#pragma resource start'
and end with '#pragma resource end'. Resources can be divided into parts
and these parts can be placed anywhere convenient. (But do not place them
in the commentary block and then be surprised when they do not get
compiled). The compiler will collect and compile the parts.
The names of the operators can be written in upper or lower case, but
the names of the identifiers are case-sensitive. Directives and
commentaries can be used in the text of the resources.
There is nothing to stop you from using compiler resources from other
languages. It is important that the syntax of the resource file
correspond to the compiler.