Ada Programming/Platform/Windows/Visual C++ - GNAT interface



This is a guide for interfacing from Ada to C++ in Windows, using GNAT and MS Visual C++.

Ada. Time-tested, safe and secure.
Ada. Time-tested, safe and secure.

Before starting, you should check your environment. This was the environment used while writing this guide.

  1. Ada GPL 2006 and GPS.
  2. Microsoft Visual Studio 2005

These are the steps we will follow:

  1. write your Ada code and build dynamic library
  2. make a .lib file (for static linking)
  3. work together
  4. run your code

Step 1. Write Ada source code

edit
----------------------------- t.ads
 with interfaces.c;
 package t is
 
    package C renames interfaces.c;
 
    procedure test(a,b,z,d,e:c.int) ;
    pragma export (stdcall, test, "test");
 
 end t;
--------------------------------t.adb

 with ada.text_io;
 with t.th;
 package body t is
    procedure test(a,b,z,d,e:c.int) is
       myd : integer := 0;
    begin
       t.th.t.getToken (myd);
       ada.text_io.put_line(myd'img & " and e value is" & c.int'image(e));
    end;
 
 end t;
------------------------------------th.ads
 package t.th is
 
    task T is
       entry getToken (t : out integer);
    end;
 private
 d : integer := 0;
 
 end t.th;
-------------------------------------th.adb

 with ada.text_io;
 package body t.th is
    task body T is
       use ada.text_io;
 
    begin
 
       loop
          select
             accept getToken (t : out integer) do
                t := d;
             end getToken;
          or
             terminate;
          end select;
          d := d + 1;
       end loop;
 
    end ;
 end t.th;

And most important thing is your project file. It make you more easy.

project Testdll is
   for Library_Name use "Te";
   for Library_Dir use "dll";
   for Library_Ali_Dir use "ali";
   for Library_Kind use "dynamic";
   for Languages use ("Ada");
   for Object_Dir use "obj";
   for Library_Interface use ("t");

   for Library_Auto_Init use "False";
   for Library_Src_Dir use "dll";
end Testdll;

You can configure almost all settings in your GPS IDE. But note that GPS cannot do Library_Interface setting correctly. After configuring it, you should check the project file and modify it accordingly. Library_Interface means which package contains export functions in DLL.

Library_Auto_Init is interesting and you must choose a correct setting in your project.

If you choose True, it will call adaInit when library load (which means elaboration appears in loading time automatically) if choose False, it will export init and finalize functions in DLL and you must call them by yourself.

If you export functions like C/C++ "normal" functions without any elaboration parts, you may set library_auto_init to True.

If your Ada code does not only work like C/C++ but also needs Ada elaboration function, you should initialize/finalize by yourself.

"DANGEROUS" DANGEROUS" DANGEROUS"DANGEROUS"DANGEROUS"

For example, if you have a library level task needing elaboration in load time and use implied linking and loading it will "deadlock WAITING" when DllMain calls init code. It's unexpected behaviour that you don't want to.

DANGEROUS"DANGEROUS"DANGEROUS"DANGEROUS"DANGEROUS"

In some case, you may use LoadLibrary / GetProcAddress to work (Explicit loading). But it is not easy to manage your code, especially if you export many functions.

Step 2. Make a .lib file (for static linking)

edit

You can use utilities dumpbin and lib to generate .lib file (2.A)

    dumpbin/exports Te.dll > te.def,

the file content is

Microsoft (R) COFF/PE Dumper Version 8.00.50727.42
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file te.dll

File Type: DLL
 Section contains the following exports for Te.dll
   00000000 characteristics
   44E2ABD2 time date stamp Wed Aug 16 13:23:30 2006
       0.00 version
          1 ordinal base
         19 number of functions
         19 number of names

   ordinal hint RVA      name

         1    0 00001461 Tefinal
         2    1 000011A0 Teinit
         3    2 00005130 _CRT_MT
         4    3 000050E0 t_E
         5    4 000050D0 t__th_E
         6    5 0000155F t__th__P2sIP
         7    6 00007040 t__th___chain
         8    7 00001745 t__th___elabb
         9    8 0000156E t__th___elabs
        10    9 00007044 t__th___master
        11    A 000050D8 t__th__d
        12    B 00007048 t__th__t
        13    C 00006010 t__th__tT1
        14    D 000016B9 t__th__tTKB
        15    E 000050D1 t__th__tTKE
        16    F 00001490 t__th__tTKVIP
        17   10 000050D4 t__th__tTKZ
        18   11 00005000 temain_E
        19   12 0000178F test@20

 Summary

       1000 .bss
       1000 .data
       1000 .edata
       2000 .idata
       1000 .rdata
       1000 .reloc
      2E000 .stab
      E3000 .stabstr
       4000 .text

(2.b) remove all in Te.def but leave ...

EXPORTS
         Tefinal
         Teinit
         _CRT_MT
         t_E
         t___elabb
         t__th_E
         t__th__P2sIP
         t__th___elabb
         t__th__d
         t__th__tB
         t__th__tE
         t__th__tVIP
         t__th__tZ
         temain_E
         test@20

The Teinit and Tefinal is used to elaborate and terminate the Ada elaboration part after Te.dll loaded. (the name does not call adainit/adafinalize but still the same function as adainit/adafinalize)

Step 3. Work together in MS VC++

edit

Start Visual Studio C++ project and add Te.lib to your linker setting.

Writing a Te.h header for Ada DLL (GPS should generate .lib and .h for you).

extern "C" extern void _stdcall test(int a,int b,int c,int d,int e);
extern "C" extern void  Teinit();
extern "C" extern void  Tefinal();

And write a C/C++ test code to generate a .exe file

#include "stdafx.h" //visual c++ default header
#include "Te.h" //your ada library header
 
int _tmain(int argc, _TCHAR* argv[])
{
 
  Teinit();  //start init
  for (int i = 0 ; i< 300 ;i++)
     test(0,0,0,0,i);
 
  Tefinal(); //end  init
  return 0;
 
}

Step 4. Run your code

edit

Put .exe and .dll in the same directory and run the test

The loader will find dll following in this order:

  1. current directory
  2. path variable
  3. Windows directory

Put the dll and exe in the same directory, it is the best option.