[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This chapter describes topics that are specific to the Microsoft Windows platforms (NT, 2000, and XP Professional).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
One of the strengths of the GNAT technology is that its tool set
(gcc
, gnatbind
, gnatlink
, gnatmake
, the
gdb
debugger, etc.) is used in the same way regardless of the
platform.
On Windows this tool set is complemented by a number of Microsoft-specific tools that have been provided to facilitate interoperability with Windows when this is required. With these tools:
CONSOLE
or WINDOWS
subsystems.
Immediately below are listed all known general GNAT-for-Windows restrictions. Other restrictions about specific features like Windows Resources and DLLs are listed in separate sections below.
GetLastError
and SetLastError
when tasking, protected records, or exceptions are used. In these
cases, in order to implement Ada semantics, the GNAT run-time system
calls certain Win32 routines that set the last error variable to 0 upon
success. It should be possible to use GetLastError
and
SetLastError
when tasking, protected record, and exception
features are not used, but it is not guaranteed to work.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Make sure the system on which GNAT is installed is accessible from the
current machine, i.e. the install location is shared over the network.
Shared resources are accessed on Windows by means of UNC paths, which
have the format \\server\sharename\path
In order to use such a network installation, simply add the UNC path of the `bin' directory of your GNAT installation in front of your PATH. For example, if GNAT is installed in `\GNAT' directory of a share location called `c-drive' on a machine `LOKI', the following command will make it available:
path \\loki\c-drive\gnat\bin;%path%
Be aware that every compilation using the network installation results in the transfer of large amounts of data across the network and will likely cause serious performance penalty.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
There are two main subsystems under Windows. The CONSOLE
subsystem
(which is the default subsystem) will always create a console when
launching the application. This is not something desirable when the
application has a Windows GUI. To get rid of this console the
application must be using the WINDOWS
subsystem. To do so
the `-mwindows' linker option must be specified.
$ gnatmake winprog -largs -mwindows |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
It is possible to control where temporary files gets created by setting the TMP environment variable. The file will be created:
This allows you to determine exactly where the temporary file will be created. This is particularly useful in networked environments where you may not have write access to some directories.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Developing pure Ada applications on Windows is no different than on other GNAT-supported platforms. However, when developing or porting an application that contains a mix of Ada and C/C++, the choice of your Windows C/C++ development environment conditions your overall interoperability strategy.
If you use gcc
to compile the non-Ada part of your application,
there are no Windows-specific restrictions that affect the overall
interoperability with your Ada code. If you plan to use
Microsoft tools (e.g. Microsoft Visual C/C++), you should be aware of
the following limitations:
.tls
section (Thread Local
Storage section) since the GNAT linker does not yet support this section.
msvcrt.dll
. This is because the GNAT run time
uses the services of msvcrt.dll
for its I/Os. Use of other I/O
libraries can cause a conflict with msvcrt.dll
services. For
instance Visual C++ I/O stream routines conflict with those in
msvcrt.dll
.
If you do want to use the Microsoft tools for your non-Ada code and hit one of the above limitations, you have two choices:
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
F.6.1 C
Calling ConventionF.6.2 Stdcall
Calling ConventionF.6.3 DLL
Calling Convention
When a subprogram F
(caller) calls a subprogram G
(callee), there are several ways to push G
's parameters on the
stack and there are several possible scenarios to clean up the stack
upon G
's return. A calling convention is an agreed upon software
protocol whereby the responsibilities between the caller (F
) and
the callee (G
) are clearly defined. Several calling conventions
are available for Windows:
C
(Microsoft defined)
Stdcall
(Microsoft defined)
DLL
(GNAT specific)
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
C
Calling Convention
This is the default calling convention used when interfacing to C/C++
routines compiled with either gcc
or Microsoft Visual C++.
In the C
calling convention subprogram parameters are pushed on the
stack by the caller from right to left. The caller itself is in charge of
cleaning up the stack after the call. In addition, the name of a routine
with C
calling convention is mangled by adding a leading underscore.
The name to use on the Ada side when importing (or exporting) a routine
with C
calling convention is the name of the routine. For
instance the C function:
int get_val (long); |
should be imported from Ada as follows:
function Get_Val (V : Interfaces.C.long) return Interfaces.C.int; pragma Import (C, Get_Val, External_Name => "get_val"); |
Note that in this particular case the External_Name
parameter could
have been omitted since, when missing, this parameter is taken to be the
name of the Ada entity in lower case. When the Link_Name
parameter
is missing, as in the above example, this parameter is set to be the
External_Name
with a leading underscore.
When importing a variable defined in C, you should always use the C
calling convention unless the object containing the variable is part of a
DLL (in which case you should use the DLL
calling convention,
see section F.6.3 DLL
Calling Convention).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Stdcall
Calling Convention This convention, which was the calling convention used for Pascal programs, is used by Microsoft for all the routines in the Win32 API for efficiency reasons. It must be used to import any routine for which this convention was specified.
In the Stdcall
calling convention subprogram parameters are pushed
on the stack by the caller from right to left. The callee (and not the
caller) is in charge of cleaning the stack on routine exit. In addition,
the name of a routine with Stdcall
calling convention is mangled by
adding a leading underscore (as for the C
calling convention) and a
trailing @
nn
, where nn is the overall size (in
bytes) of the parameters passed to the routine.
The name to use on the Ada side when importing a C routine with a
Stdcall
calling convention is the name of the C routine. The leading
underscore and trailing @
nn
are added automatically by
the compiler. For instance the Win32 function:
APIENTRY int get_val (long); |
should be imported from Ada as follows:
function Get_Val (V : Interfaces.C.long) return Interfaces.C.int; pragma Import (Stdcall, Get_Val); -- On the x86 a long is 4 bytes, so the Link_Name is "_get_val@4" |
As for the C
calling convention, when the External_Name
parameter is missing, it is taken to be the name of the Ada entity in lower
case. If instead of writing the above import pragma you write:
function Get_Val (V : Interfaces.C.long) return Interfaces.C.int; pragma Import (Stdcall, Get_Val, External_Name => "retrieve_val"); |
then the imported routine is _retrieve_val@4
. However, if instead
of specifying the External_Name
parameter you specify the
Link_Name
as in the following example:
function Get_Val (V : Interfaces.C.long) return Interfaces.C.int; pragma Import (Stdcall, Get_Val, Link_Name => "retrieve_val"); |
then the imported routine is retrieve_val@4
, that is, there is no
trailing underscore but the appropriate @
nn
is always
added at the end of the Link_Name
by the compiler.
Note, that in some special cases a DLL's entry point name lacks a trailing
@
nn
while the exported name generated for a call has it.
The gnatdll
tool, which creates the import library for the DLL, is able
to handle those cases (see section F.11.7 Using gnatdll
for the description of
the switches).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
DLL
Calling Convention
This convention, which is GNAT-specific, must be used when you want to
import in Ada a variables defined in a DLL. For functions and procedures
this convention is equivalent to the Stdcall
convention. As an
example, if a DLL contains a variable defined as:
int my_var; |
then, to access this variable from Ada you should write:
My_Var : Interfaces.C.int; pragma Import (DLL, My_Var); |
The remarks concerning the External_Name
and Link_Name
parameters given in the previous sections equally apply to the DLL
calling convention.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A Dynamically Linked Library (DLL) is a library that can be shared by several applications running under Windows. A DLL can contain any number of routines and variables.
One advantage of DLLs is that you can change and enhance them without forcing all the applications that depend on them to be relinked or recompiled. However, you should be aware than all calls to DLL routines are slower since, as you will understand below, such calls are indirect.
To illustrate the remainder of this section, suppose that an application wants to use the services of a DLL `API.dll'. To use the services provided by `API.dll' you must statically link against the DLL or an import library which contains a jump table with an entry for each routine and variable exported by the DLL. In the Microsoft world this import library is called `API.lib'. When using GNAT this import library is called either `libAPI.a' or `libapi.a' (names are case insensitive).
After you have linked your application with the DLL or the import library and you run your application, here is what happens:
DllMain
or
DllMainCRTStartup
are invoked. These routines typically contain
the initialization code needed for the well-being of the routines and
variables exported by the DLL.
There is an additional point which is worth mentioning. In the Windows world there are two kind of DLLs: relocatable and non-relocatable DLLs. Non-relocatable DLLs can only be loaded at a very specific address in the target application address space. If the addresses of two non-relocatable DLLs overlap and these happen to be used by the same application, a conflict will occur and the application will run incorrectly. Hence, when possible, it is always preferable to use and build relocatable DLLs. Both relocatable and non-relocatable DLLs are supported by GNAT. Note that the `-s' linker option (see GNU Linker User's Guide) removes the debugging symbols from the DLL but the DLL can still be relocated.
As a side note, an interesting difference between Microsoft DLLs and Unix shared libraries, is the fact that on most Unix systems all public routines are exported by default in a Unix shared library, while under Windows it is possible (but not required) to list exported routines in a definition file (see section F.8.2.1 The Definition File).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
F.8.1 Creating an Ada Spec for the DLL Services F.8.2 Creating an Import Library
To use the services of a DLL, say `API.dll', in your Ada application you must have:
Once you have all the above, to compile an Ada application that uses the
services of `API.dll' and whose main subprogram is My_Ada_App
,
you simply issue the command
$ gnatmake my_ada_app -largs -lAPI |
The argument `-largs -lAPI' at the end of the gnatmake
command
tells the GNAT linker to look first for a library named `API.lib'
(Microsoft-style name) and if not found for a library named `libAPI.a'
(GNAT-style name). Note that if the Ada package spec for `API.dll'
contains the following pragma
pragma Linker_Options ("-lAPI"); |
you do not have to add `-largs -lAPI' at the end of the
gnatmake
command.
If any one of the items above is missing you will have to create it yourself. The following sections explain how to do so using as an example a fictitious DLL called `API.dll'.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A DLL typically comes with a C/C++ header file which provides the definitions of the routines and variables exported by the DLL. The Ada equivalent of this header file is a package spec that contains definitions for the imported entities. If the DLL you intend to use does not come with an Ada spec you have to generate one such spec yourself. For example if the header file of `API.dll' is a file `api.h' containing the following two definitions:
int some_var; int get (char *); |
then the equivalent Ada spec could be:
with Interfaces.C.Strings; package API is use Interfaces; Some_Var : C.int; function Get (Str : C.Strings.Chars_Ptr) return C.int; private pragma Import (C, Get); pragma Import (DLL, Some_Var); end API; |
Note that a variable is always imported with a DLL convention. A
function can have C
, Stdcall
or DLL
convention. For
subprograms, the DLL
convention is a synonym of Stdcall
(see section F.6 Windows Calling Conventions).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
F.8.2.1 The Definition File F.8.2.2 GNAT-Style Import Library F.8.2.3 Microsoft-Style Import Library
If a Microsoft-style import library `API.lib' or a GNAT-style import library `libAPI.a' is available with `API.dll' you can skip this section. You can also skip this section if `API.dll' is built with GNU tools as in this case it is possible to link directly against the DLL. Otherwise read on.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
As previously mentioned, and unlike Unix systems, the list of symbols
that are exported from a DLL must be provided explicitly in Windows.
The main goal of a definition file is precisely that: list the symbols
exported by a DLL. A definition file (usually a file with a .def
suffix) has the following structure:
[LIBRARY name] [DESCRIPTION string] EXPORTS symbol1 symbol2 ... |
LIBRARY name
DESCRIPTION string
EXPORTS
EXPORTS
section of `API.def' looks like:
EXPORTS some_var get |
Note that you must specify the correct suffix (@
nn
)
(see section F.6 Windows Calling Conventions) for a Stdcall
calling convention function in the exported symbols list.
There can actually be other sections in a definition file, but these sections are not relevant to the discussion at hand.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
To create a static import library from `API.dll' with the GNAT tools you should proceed as follows:
dll2def
tool as follows:
$ dll2def API.dll > API.def |
dll2def
is a very simple tool: it takes as input a DLL and prints
to standard output the list of entry points in the DLL. Note that if
some routines in the DLL have the Stdcall
convention
(see section F.6 Windows Calling Conventions) with stripped @
nn
suffix then you'll have to edit `api.def' to add it, and specify
-k
to gnatdll
when creating the import library.
Here are some hints to find the right @
nn suffix.
dumpbin
tool (see the
corresponding Microsoft documentation for further details).
$ dumpbin /exports api.lib |
libAPI.a
, using gnatdll
(see section F.11.7 Using gnatdll
) as follows:
$ gnatdll -e API.def -d API.dll |
gnatdll
takes as input a definition file `API.def' and the
name of the DLL containing the services listed in the definition file
`API.dll'. The name of the static import library generated is
computed from the name of the definition file as follows: if the
definition file name is xyz.def
, the import library name will
be lib
xyz.a
. Note that in the previous example option
`-e' could have been removed because the name of the definition
file (before the ".def
" suffix) is the same as the name of the
DLL (see section F.11.7 Using gnatdll
for more information about gnatdll
).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
With GNAT you can either use a GNAT-style or Microsoft-style import library. A Microsoft import library is needed only if you plan to make an Ada DLL available to applications developed with Microsoft tools (see section F.5 Mixed-Language Programming on Windows).
To create a Microsoft-style import library for `API.dll' you should proceed as follows:
dll2def
tool as described above or the Microsoft dumpbin
tool (see the corresponding Microsoft documentation for further details).
lib
utility:
$ lib -machine:IX86 -def:API.def -out:API.lib |
If you use the above command the definition file `API.def' must contain a line giving the name of the DLL:
LIBRARY "API" |
See the Microsoft documentation for further details about the usage of
lib
.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This section explain how to build DLLs using the GNAT built-in DLL support. With the following procedure it is straight forward to build and use DLLs with GNAT.
The first step is to build all objects files that are to be included
into the DLL. This is done by using the standard gnatmake
tool.
To build the DLL you must use gcc
's -shared
option. It is quite simple to use this method:
$ gcc -shared -o api.dll obj1.o obj2.o ... |
It is important to note that in this case all symbols found in the
object files are automatically exported. It is possible to restrict
the set of symbols to export by passing to gcc
a definition
file, see section F.8.2.1 The Definition File. For example:
$ gcc -shared -o api.dll api.def obj1.o obj2.o ... |
If you use a definition file you must export the elaboration procedures for every package that required one. Elaboration procedures are named using the package name followed by "_E".
For the DLL to be used by client programs the bodies must be hidden from it and the .ali set with read-only attribute. This is very important otherwise GNAT will recompile all packages and will not actually use the code in the DLL. For example:
$ mkdir apilib $ copy *.ads *.ali api.dll apilib $ attrib +R apilib\*.ali |
At this point it is possible to use the DLL by directly linking
against it. Note that you must use the GNAT shared runtime when using
GNAT shared libraries. This is achieved by using -shared
binder's
option.
$ gnatmake main -Iapilib -bargs -shared -largs -Lapilib -lAPI |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
There is nothing specific to Windows in this area. see section 11.12 Library Projects.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Note that it is prefered to use the built-in GNAT DLL support (see section F.9 Building DLLs with GNAT) or GNAT Project files (see section F.10 Building DLLs with GNAT Project files) to build DLLs.
This section explains how to build DLLs containing Ada code using
gnatdll
. These DLLs will be referred to as Ada DLLs in the
remainder of this section.
The steps required to build an Ada DLL that is to be used by Ada as well as non-Ada applications are as follows:
C
or
Stdcall
calling convention to avoid any Ada name mangling for the
entities exported by the DLL (see section F.11.2 Exporting Ada Entities). You can
skip this step if you plan to use the Ada DLL only from Ada applications.
adainit
generated by gnatbind
to perform the elaboration of
the Ada code in the DLL (see section F.11.3 Ada DLLs and Elaboration). The initialization
routine exported by the Ada DLL must be invoked by the clients of the DLL
to initialize the DLL.
adafinal
generated by gnatbind
to perform the
finalization of the Ada code in the DLL (see section F.11.4 Ada DLLs and Finalization).
The finalization routine exported by the Ada DLL must be invoked by the
clients of the DLL when the DLL services are no further needed.
gnatdll
to produce the DLL and the import
library (see section F.11.7 Using gnatdll
).
Note that a relocatable DLL stripped using the strip
binutils
tool will not be relocatable anymore. To build a DLL without debug
information pass -largs -s
to gnatdll
.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
When using Ada DLLs from Ada applications there is a limitation users should be aware of. Because on Windows the GNAT run time is not in a DLL of its own, each Ada DLL includes a part of the GNAT run time. Specifically, each Ada DLL includes the services of the GNAT run time that are necessary to the Ada code inside the DLL. As a result, when an Ada program uses an Ada DLL there are two independent GNAT run times: one in the Ada DLL and one in the main program.
It is therefore not possible to exchange GNAT run-time objects between the
Ada DLL and the main Ada program. Example of GNAT run-time objects are file
handles (e.g. Text_IO.File_Type
), tasks types, protected objects
types, etc.
It is completely safe to exchange plain elementary, array or record types, Windows object handles, etc.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Building a DLL is a way to encapsulate a set of services usable from any
application. As a result, the Ada entities exported by a DLL should be
exported with the C
or Stdcall
calling conventions to avoid
any Ada name mangling. Please note that the Stdcall
convention
should only be used for subprograms, not for variables. As an example here
is an Ada package API
, spec and body, exporting two procedures, a
function, and a variable:
with Interfaces.C; use Interfaces; package API is Count : C.int := 0; function Factorial (Val : C.int) return C.int; procedure Initialize_API; procedure Finalize_API; -- Initialization & Finalization routines. More in the next section. private pragma Export (C, Initialize_API); pragma Export (C, Finalize_API); pragma Export (C, Count); pragma Export (C, Factorial); end API; |
package body API is function Factorial (Val : C.int) return C.int is Fact : C.int := 1; begin Count := Count + 1; for K in 1 .. Val loop Fact := Fact * K; end loop; return Fact; end Factorial; procedure Initialize_API is procedure Adainit; pragma Import (C, Adainit); begin Adainit; end Initialize_API; procedure Finalize_API is procedure Adafinal; pragma Import (C, Adafinal); begin Adafinal; end Finalize_API; end API; |
If the Ada DLL you are building will only be used by Ada applications
you do not have to export Ada entities with a C
or Stdcall
convention. As an example, the previous package could be written as
follows:
package API is Count : Integer := 0; function Factorial (Val : Integer) return Integer; procedure Initialize_API; procedure Finalize_API; -- Initialization and Finalization routines. end API; |
package body API is function Factorial (Val : Integer) return Integer is Fact : Integer := 1; begin Count := Count + 1; for K in 1 .. Val loop Fact := Fact * K; end loop; return Fact; end Factorial; ... -- The remainder of this package body is unchanged. end API; |
Note that if you do not export the Ada entities with a C
or
Stdcall
convention you will have to provide the mangled Ada names
in the definition file of the Ada DLL
(see section F.11.6 Creating the Definition File).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The DLL that you are building contains your Ada code as well as all the routines in the Ada library that are needed by it. The first thing a user of your DLL must do is elaborate the Ada code (see section C. Elaboration Order Handling in GNAT).
To achieve this you must export an initialization routine
(Initialize_API
in the previous example), which must be invoked
before using any of the DLL services. This elaboration routine must call
the Ada elaboration routine adainit
generated by the GNAT binder
(see section 4.2.5 Binding with Non-Ada Main Programs). See the body of
Initialize_Api
for an example. Note that the GNAT binder is
automatically invoked during the DLL build process by the gnatdll
tool (see section F.11.7 Using gnatdll
).
When a DLL is loaded, Windows systematically invokes a routine called
DllMain
. It would therefore be possible to call adainit
directly from DllMain
without having to provide an explicit
initialization routine. Unfortunately, it is not possible to call
adainit
from the DllMain
if your program has library level
tasks because access to the DllMain
entry point is serialized by
the system (that is, only a single thread can execute "through" it at a
time), which means that the GNAT run time will deadlock waiting for the
newly created task to complete its initialization.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
When the services of an Ada DLL are no longer needed, the client code should
invoke the DLL finalization routine, if available. The DLL finalization
routine is in charge of releasing all resources acquired by the DLL. In the
case of the Ada code contained in the DLL, this is achieved by calling
routine adafinal
generated by the GNAT binder
(see section 4.2.5 Binding with Non-Ada Main Programs).
See the body of Finalize_Api
for an
example. As already pointed out the GNAT binder is automatically invoked
during the DLL build process by the gnatdll
tool
(see section F.11.7 Using gnatdll
).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
To use the services exported by the Ada DLL from another programming
language (e.g. C), you have to translate the specs of the exported Ada
entities in that language. For instance in the case of API.dll
,
the corresponding C header file could look like:
extern int *_imp__count; #define count (*_imp__count) int factorial (int); |
It is important to understand that when building an Ada DLL to be used by
other Ada applications, you need two different specs for the packages
contained in the DLL: one for building the DLL and the other for using
the DLL. This is because the DLL
calling convention is needed to
use a variable defined in a DLL, but when building the DLL, the variable
must have either the Ada
or C
calling convention. As an
example consider a DLL comprising the following package API
:
package API is Count : Integer := 0; ... -- Remainder of the package omitted. end API; |
After producing a DLL containing package API
, the spec that
must be used to import API.Count
from Ada code outside of the
DLL is:
package API is Count : Integer; pragma Import (DLL, Count); end API; |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The definition file is the last file needed to build the DLL. It lists
the exported symbols. As an example, the definition file for a DLL
containing only package API
(where all the entities are exported
with a C
calling convention) is:
EXPORTS count factorial finalize_api initialize_api |
If the C
calling convention is missing from package API
,
then the definition file contains the mangled Ada names of the above
entities, which in this case are:
EXPORTS api__count api__factorial api__finalize_api api__initialize_api |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
gnatdll
F.11.7.1 gnatdll
ExampleF.11.7.2 gnatdll
behind the ScenesF.11.7.3 Using dlltool
gnatdll
is a tool to automate the DLL build process once all the Ada
and non-Ada sources that make up your DLL have been compiled.
gnatdll
is actually in charge of two distinct tasks: build the
static import library for the DLL and the actual DLL. The form of the
gnatdll
command is
$ gnatdll [switches] list-of-files [-largs opts] |
where list-of-files is a list of ALI and object files. The object file list must be the exact list of objects corresponding to the non-Ada sources whose services are to be included in the DLL. The ALI file list must be the exact list of ALI files for the corresponding Ada sources whose services are to be included in the DLL. If list-of-files is missing, only the static import library is generated.
You may specify any of the following switches to gnatdll
:
-a[address]
gnatdll
builds relocatable DLL. We
advise the reader to build relocatable DLL.
-b address
-bargs opts
-d dllfile
gnatdll
to do anything. The name of the generated import library is
obtained algorithmically from dllfile as shown in the following
example: if dllfile is xyz.dll
, the import library name is
libxyz.a
. The name of the definition file to use (if not specified
by option `-e') is obtained algorithmically from dllfile
as shown in the following example:
if dllfile is xyz.dll
, the definition
file used is xyz.def
.
-e deffile
-g
-h
gnatdll
switch usage information.
-Idir
gnatdll
to search the dir directory for source and
object files needed to build the DLL.
(see section 3.3 Search Paths and the Run-Time Library (RTL)).
-k
@
nn suffix from the import library's exported
names, but keeps them for the link names. You must specify this
option if you want to use a Stdcall
function in a DLL for which
the @
nn suffix has been removed. This is the case for most
of the Windows NT DLL for example. This option has no effect when
`-n' option is specified.
-l file
-n
-q
-v
-largs opts
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
gnatdll
Example As an example the command to build a relocatable DLL from `api.adb' once `api.adb' has been compiled and `api.def' created is
$ gnatdll -d api.dll api.ali |
The above command creates two files: `libapi.a' (the import library) and `api.dll' (the actual DLL). If you want to create only the DLL, just type:
$ gnatdll -d api.dll -n api.ali |
Alternatively if you want to create just the import library, type:
$ gnatdll -d api.dll |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
gnatdll
behind the Scenes
This section details the steps involved in creating a DLL. gnatdll
does these steps for you. Unless you are interested in understanding what
goes on behind the scenes, you should skip this section.
We use the previous example of a DLL containing the Ada package API
,
to illustrate the steps necessary to build a DLL. The starting point is a
set of objects that will make up the DLL and the corresponding ALI
files. In the case of this example this means that `api.o' and
`api.ali' are available. To build a relocatable DLL, gnatdll
does
the following:
gnatdll
builds the base file (`api.base'). A base file gives
the information necessary to generate relocation information for the
DLL.
$ gnatbind -n api $ gnatlink api -o api.jnk -mdll -Wl,--base-file,api.base |
In addition to the base file, the gnatlink
command generates an
output file `api.jnk' which can be discarded. The `-mdll' switch
asks gnatlink
to generate the routines DllMain
and
DllMainCRTStartup
that are called by the Windows loader when the DLL
is loaded into memory.
gnatdll
uses dlltool
(see section F.11.7.3 Using dlltool
) to build the
export table (`api.exp'). The export table contains the relocation
information in a form which can be used during the final link to ensure
that the Windows loader is able to place the DLL anywhere in memory.
$ dlltool --dllname api.dll --def api.def --base-file api.base \ --output-exp api.exp |
gnatdll
builds the base file using the new export table. Note that
gnatbind
must be called once again since the binder generated file
has been deleted during the previous call to gnatlink
.
$ gnatbind -n api $ gnatlink api -o api.jnk api.exp -mdll -Wl,--base-file,api.base |
gnatdll
builds the new export table using the new base file and
generates the DLL import library `libAPI.a'.
$ dlltool --dllname api.dll --def api.def --base-file api.base \ --output-exp api.exp --output-lib libAPI.a |
gnatdll
builds the relocatable DLL using the final export
table.
$ gnatbind -n api $ gnatlink api api.exp -o api.dll -mdll |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
dlltool
dlltool
is the low-level tool used by gnatdll
to build
DLLs and static import libraries. This section summarizes the most
common dlltool
switches. The form of the dlltool
command
is
$ dlltool [switches] |
dlltool
switches include:
dlltool
with switch
`--output-lib'.
@
nn from exported names
(see section F.6 Windows Calling Conventions
for a discussion about Stdcall
-style symbols.
dlltool
switches with a concise description.
as
.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
F.12.1 Building Resources F.12.2 Compiling Resources F.12.3 Using Resources
Resources are an easy way to add Windows specific objects to your application. The objects that can be added as resources include:
This section explains how to build, compile and use resources.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A resource file is an ASCII file. By convention resource files have an
`.rc' extension.
The easiest way to build a resource file is to use Microsoft tools
such as imagedit.exe
to build bitmaps, icons and cursors and
dlgedit.exe
to build dialogs.
It is always possible to build an `.rc' file yourself by writing a
resource script.
It is not our objective to explain how to write a resource file. A complete description of the resource script language can be found in the Microsoft documentation.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This section describes how to build a GNAT-compatible (COFF) object file
containing the resources. This is done using the Resource Compiler
windres
as follows:
$ windres -i myres.rc -o myres.o |
By default windres
will run gcc
to preprocess the `.rc'
file. You can specify an alternate preprocessor (usually named
`cpp.exe') using the windres
`--preprocessor'
parameter. A list of all possible options may be obtained by entering
the command windres
`--help'.
It is also possible to use the Microsoft resource compiler rc.exe
to produce a `.res' file (binary resource file). See the
corresponding Microsoft documentation for further details. In this case
you need to use windres
to translate the `.res' file to a
GNAT-compatible object file as follows:
$ windres -i myres.res -o myres.o |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
To include the resource file in your program just add the
GNAT-compatible object file for the resource(s) to the linker
arguments. With gnatmake
this is done by using the `-largs'
option:
$ gnatmake myprog -largs myres.o |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
F.13.1 Program and DLL Both Built with GCC/GNAT F.13.2 Program Built with Foreign Tools and DLL Built with GCC/GNAT
Debugging a DLL is similar to debugging a standard program. But we have to deal with two different executable parts: the DLL and the program that uses it. We have the following four possibilities:
GCC/GNAT
.
GCC/GNAT
.
GCC/GNAT
and the DLL is built with
foreign tools.
In this section we address only cases one and two above.
There is no point in trying to debug
a DLL with GNU/GDB
, if there is no GDB-compatible debugging
information in it. To do so you must use a debugger compatible with the
tools suite used to build the DLL.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This is the simplest case. Both the DLL and the program have GDB
compatible debugging information. It is then possible to break anywhere in
the process. Let's suppose here that the main procedure is named
ada_main
and that in the DLL there is an entry point named
ada_dll
.
The DLL (see section F.7 Introduction to Dynamic Link Libraries (DLLs)) and program must have been built with the debugging information (see GNAT -g switch). Here are the step-by-step instructions for debugging it:
GDB
on the main program.
$ gdb -nw ada_main |
(gdb) break ada_main (gdb) run |
This step is required to be able to set a breakpoint inside the DLL. As long as the program is not run, the DLL is not loaded. This has the consequence that the DLL debugging information is also not loaded, so it is not possible to set a breakpoint in the DLL.
(gdb) break ada_dll (gdb) run |
At this stage a breakpoint is set inside the DLL. From there on you can use the standard approach to debug the whole program (see section 24. Running and Debugging Ada Programs).
To break on the DllMain
routine it is not possible to follow
the procedure above. At the time the program stop on ada_main
the DllMain
routine as already been called. Either you can use
the procedure below see section F.13.2.1 Debugging the DLL Directly or this procedure:
GDB
on the main program.
$ gdb -nw ada_main |
(gdb) add-sym api.dll |
(gdb) break ada_dll.adb:45 |
Note that at this point it is not possible to break using the routine symbol directly as the program is not yet running. The solution is to break on the proper line (break in `ada_dll.adb' line 45).
(gdb) run |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
F.13.2.1 Debugging the DLL Directly F.13.2.2 Attaching to a Running Process
In this case things are slightly more complex because it is not possible to
start the main program and then break at the beginning to load the DLL and the
associated DLL debugging information. It is not possible to break at the
beginning of the program because there is no GDB
debugging information,
and therefore there is no direct way of getting initial control. This
section addresses this issue by describing some methods that can be used
to break somewhere in the DLL to debug it.
First suppose that the main procedure is named main
(this is for
example some C code built with Microsoft Visual C) and that there is a
DLL named test.dll
containing an Ada entry point named
ada_dll
.
The DLL (see section F.7 Introduction to Dynamic Link Libraries (DLLs)) must have been built with debugging information (see GNAT -g option).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
$ gdb -nw test.dll |
(gdb) break ada_dll.adb:45 |
Note that at this point it is not possible to break using the routine symbol directly as the program is not yet running. The solution is to break on the proper line (break in `ada_dll.adb' line 45).
GDB
.
(gdb) exec-file main.exe |
(gdb) run |
This will run the program until it reaches the breakpoint that has been set. From that point you can use the standard way to debug a program as described in (see section 24. Running and Debugging Ada Programs).
It is also possible to debug the DLL by attaching to a running process.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
With GDB
it is always possible to debug a running process by
attaching to it. It is possible to debug a DLL this way. The limitation
of this approach is that the DLL must run long enough to perform the
attach operation. It may be useful for instance to insert a time wasting
loop in the code of the DLL to meet this criterion.
$ main |
$ gdb -nw |
(gdb) attach 208 |
(gdb) symbol-file main.exe |
(gdb) break ada_dll |
(gdb) continue |
This last step will resume the process execution, and stop at the breakpoint we have set. From there you can use the standard approach to debug a program as described in (see section 24. Running and Debugging Ada Programs).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This section is temporarily left blank.
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |