noddy.gif (15143 bytes)The Noddy Guide to using ADA code with Visual Basic

 

To judge from postings in the comp.lang.ada newsgroup, this might be an FAQ:

You have some code that you don't want to rewrite which is written in ADA. You want to stick a GUI interface on it. So you want to compile the ADA into a Microsoft Windows Dynamic Link Library (DLL) and then call it from Visual Basic. How do you do it?

No-one seems to know.  Here are the results of my investigations - I am part way there.

The other alternative (advocated by AdaPower) is to create a COM object using the GNATCOM utilities from their site.  This sounds as if it might be easier than what I'm doing.


This page is the result of my own investigations into the subject. I am not an ADA  person, so this will be pretty superficial. However I think the page is necessary as the existing pages do not seem to be focused on people like myself, which means they are hard to use, however useful they may be to the guru.

This page is not kept up to date - use it with that in mind. This is simply because I have other things to do. 


Ada Compilers - the good, the bad and the ugly

It's a strange feature of the world of Ada that even vendors seem to try to keep things hidden. You can look at their web pages for ages without finding out how to get their products. So here's the details.

There are two Ada compilers:

A cut-down version is in fact available at no charge. For all I know you can download it from somewhere. I acquired this on the free CDROM with Simon Johnston's Ada for C and C++ Programmers, Addison-Wesley 1997. It will not do DLL's, and has a (fairly generous) limit on the maximum number of files in the project, but otherwise seems to be as per the full product. It comes with a weedy GUI, but is probably best run at the command line.

The commercial version comes in a range of versions, starting with a Personal ObjectAda/Win, at around $245, and going up from there exponentially. However all do DLL's. These are much better than the GNAT ones, and they can, in principle, be called from VB. 

One nasty feature of Aonix UK - they prevent you ordering from the US, and charge you at $1=£1; and then they add VAT at 17.5%, effectively making the Personal edition cost UK customers around $400.  They also offer no support, which makes you wonder why anyone would buy, rather than pirate.

Once again there is a GUI interface to this, but really it's intended to be run from the command line.

The free version does DLL's - but there are problems with the resulting .dll files, of which more anon.


Creating and compiling a DLL with AONIX - demo

There is a demo in the help file, if you can find it.   Based on that I amended it to contain this stuff [qqq add more sometime and explain this:  The call nearly always works, but the calls with parameters are the problem.  You need to use DLL as the calling convention in the pragma - the others fail.  And you need to suppress checks.  I got that bit from the source for the AdaInit module included in the help file, which tells you that it needs to be so.]

---------------------------------- Start of File --------------------------------------
package P is

-- Header - list the procedures/functions, with their pragmas
--sub DLLProc
--------------
procedure DLLProc;
  pragma Export(DLL,DLLProc,"DLLProc");
--sub mytestfunc
----------------
function mytestfunc return integer;
  pragma Export(DLL,mytestfunc,"mytestfunc");
  pragma Suppress (All_Checks, on => mytestfunc); 
-- It doesn't get back the param without this - instead
-- you get an error (DUMP it to file!) "Runtime error '453': can't find DLL entry point mytestfunc in dll.DLL"
-- NB make sure you compile AND build! Several times I was only compiling when I thought I was building!
-- Other values that don't work that I tried:
-- pragma Export(DLL_Stdcall,mytestfunc,"mytestfunc");
-- pragma Export(ada,mytestfunc,"mytestfunc");
-- pragma Export(ada,mytestfunc,link_name => "mytestfunc");
end;      -- Of the package header
--------------------------------------------------

with Ada.Text_IO;
package body P is

-- DLL funcs and procedures here
procedure DLLProc is
    myvar : integer;
begin
    myvar := 101;
end DLLProc;
----------------------------

function mytestfunc return integer is
begin
    return 101;
end mytestfunc;
----------------------------

end P;     -- End of the package
--------------------------------------------------------
with P;
procedure DLL is
begin
   null; -- perform any initializations here
end;
-------------------------------- End of File --------------------------------------

If you need an Ada EXE to test your DLL is OK before you try calling it from VB, create a .adb containing this:

--Calling the DLL
--
procedure UseDLL is
procedure DLLProc;
pragma Import(DLL,DLLProc,"DLLProc");
begin
 DLLProc;
end;

Here's some output from the compiler for the DLL:

1. Project|Compile All Files:

--------------------Target: Win32 (Intel) Debug--------------------
standard.ada: Warning: LRM:10.1.4(5), Unable to read file C:\Roger_Big\vbinfo2\C++ and VB DLL\vbdemo\adadll2\adadll\adadll-Win32(Intel)-Debug\adadll.adb to find needed compilation unit, continuing

Front end of ..\..\adadll.adb succeeded with no errors.
obj\adb\adadll.obj
Tool execution has completed. 

2. Project|Build DLL.DLL:

--------------------Target: Win32 (Intel) Debug--------------------
ObjectAda Personal Edition version 7.1.2.806: adabuild
Copyright (c) 1997, Aonix. All Rights Reserved.
obj\elt\dll.obj
Creating library dll.lib and object dll.exp
Linking...
Link of dll completed successfully
Tool execution has completed.

So, what have I achieved?  1.  I can call a DLL from VB.  2.  I can get an integer back from it.

However, I can't pass a string, and returning a character causes a crash.  More investigation needed here.


At present the DLL's produced by GNAT can't be called from Visual Basic.  This information is here in case the situation changes.

Creating and compiling a DLL with GNAT - demo

This is not particularly complicated as such, but it's difficult to locate the information. However a simple project does exist, and here it is, thanks to Jerry van Dijk and David Marceau 3rd June 1999, who have placed it in the public domain. Thanks also to David Botton for his help.

1. The demo Ada DLL

This is a 'noddy' DLL which contains three functions; Junk() which takes no parameters and just displays a message, Junk_2() which is the same but returns a value, and Junk_3() which is the same as _2() but takes a parameter.

There are 2 files:

2. Calling the DLL from C/C++ - DEMO

In order to try out the DLL, we must call it from somewhere. Here we use a little C++ program which simply calls each function in turn.

These are the files which create a tiny C++ program which can call the DLL. I used Visual C++ 2.0 to compile them, but I gather Visual C++ 6.0 SP1 also works.

3. Compilation

To compile these a DOS batch file is used. Don't use the IDE's - do all your compiling at the command line. For MSVC this will mean running a batch file to ensure that the compiler is available at the command line. (In MSVC 2.0 you run VCVARS32.BAT from the MSVC20\BIN directory).

This requires GNAT Ada v3.11p - earlier versions will not work.

All of these files can be downloaded from the above links - they're as small as may be.

If you want to tweak the compilation options, for debug use -g and avoid -O2 in the compiler; and avoid -s in the linker.

4. Running the DEMO

At the command prompt, just type

client

and the program will run. There is no need to 'register' the DLL - since it isn't a COM object it can't be registered anyway.

5. Calling the DLL from Visual Basic

This is where it all gets difficult. Basically if you try to use that DLL which worked fine in C++, it doesn't work, and gives error 7. It seems you can only use this DLL if you call it via a .LIB file, which of course VB does not.

Opinions as to why are as follows:

David Botton:

"The GNU tools have a bug that prevents proper DLL creation on Windows 95/98. (although sometimes it works). If you create the DLL on an NT machine it works even on a Win95/98 machine. The problem is in the linking stage garbage gets pulled in to the DLL on Win95/98."

David Marceau:

"Yup been there done that. The problem is that Ada wants the dll to be loaded in some vm address that has a lot of space to it. In windows 95, the way the os gives heap/stack to each exe/dll is different than nt. And from what I can see ada asks for a lot in the dll.

"In NT, this problem doesn't arise since it can create big vm's with lots of heap/stack and if you look at a C++ debugger output while loading your dll on nt, you will see an outputdebugstring message say 'nt relocating adadll.dll'. What is happening here is the space where it tried to load the dll the first time was too small, so NT gives it another place with more space.

"Windows 95 does not do this magic. That's all I can say. It will work on NT but not on windows 95 unless it is the first dll loaded from a c/c++ client. If you load it after anything else forget it."

I've not experimented with compiling the DLL on NT and seeing if it worked with VB6 on that platform, as I need something that will work on Win9x.


Modifying the GNAT demo DLL

There are some points that need to be considered when you write a DLL.

1. Creating your own functions/procedures

Create ada code for these. It should be remembered that most things in windows use stdcall calling convention for calling routines in a dll, so you may have to have a line with use a

pragma export(stdcall,...)

There are examples below and in the demo.

2. DLLMain()

There must be a procedure of this name, created as follows:

    --
    -- * Prototype
    --
    function DllMain (A, B, C : Integer) return Integer;
    pragma Export (StdCall, DllMain, "DllMain");
    --
    -- * Function
    --
    function DllMain (A, B, C : Integer) return Integer is
    begin
      return 1;
    end DllMain;

This function is called when the dll initialises. Leave it just like this. Usually it is not safe to call Ada code from here, because elaboration (the Ada name for execution of Library and Package Body initialisation code) has not been done at the moment the routine is called. Do not call the ada elaboration routine adainit() from it, because this will make the DLL hang if tasking (the ada built-in multi-tasking) is involved in your code.

3. AdaInit()

The AdaInit() procedure is declared as follows in the header:

      procedure AdaInit;
      pragma Import (C, AdaInit);

Make sure that you call it from a procedure of your own before any 'elaboration' (i.e. package body initialisation sections) of the ada code is required. Usually it is best to add a routine in the dll interface specifically for doing this. Note that does not matter if you call this routine more then once, because the second and subsequent calls have no effect, as may be seen from the generated code from gnatbind.

These notes derived from a page by Wiljan Derks, at http://www.adapower.com/articles/howto-gdll.html


Useful Web Pages

The most useful resource is David Botton's Adapower site and in particular the article "Using GNAT Dlls from C (van Dijk & Marceau)" at http://www.adapower.com/articles/howto-gdllc.html. There is also material for the real techie on producing a COM object in Ada, inside a DLL.

There is also an Ada newsgroup - comp.lang.ada - which can be useful, particularly if you use Google Groups to search its back archive. There is no FAQ, unfortunately.

Have a look also for C++ DLL and VB.  There are some good tutorials, and many of the error messages that VB gives are the same.  Food for thought anyway.  It was after reading some of these that I felt impelled to have another go.


2024 update: a pitfall

An email from a gentleman who prefers to remain anonymous highlights a pitfall.

"I need to point out an alarmingly glaring bug in your page that the manual itself specifically says not to do. ... Perhaps you've not seen the small amount of information provided by the GNAT GCC compiler on mixed language Windows programming? Specifically this page: https://gcc.gnu.org/onlinedocs/gnat_ugn/Ada-DLLs-and-Elaboration.html

"This distinctly says, in no uncertain terms, that you are never to put the Ada runtime start and stop routines in the DLL main function. Shouldn't even work. If they worked in any version of Windows that was a fluke. They specifically say that that's a single threaded execution you will become trapped and it will not work. If you're dealing with someone else's API you'll have to find a convenient place to put in the Init and finalize operations. Please note that you can call Adainit multiple times without repercussion.

"Otherwise I had nothing else.  The commands you posted are still correct and relevant GNAT/GCC commands, even with the newest stuff from this year."

This page is now 23 years in my past, and I have no interest in digging into it all once more.  But thank you, sir, for the correction!

Constructive feedback is welcomed to Roger Pearse.

Addition 24th May 2024.
Revised 26th October 2001.  
Revised 14th July 1999.

This page has been online since 11th December 1999.

Return to Roger Pearse's Pages