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.