-1

I created an Ada DLL that contains a function that returns the sum of the two integers given to it. I am trying to implement an Ada program that loads the Ada DLL, calls the function with two numbers, and then prints the result on the console.

with Addition;
use Addition;
with Ada.Text_IO;
use Ada.Text_IO;


procedure loader is

pragma Linker_Options ("Addition.dll"); --ada dll 

begin

Adder(4,3);
Put_Line("Adder");

end loader;

Giving me error => can't use function in procedure call

Can someone tell me how to call Ada dll function in another Ada program?

Thank you.

Elydasian
  • 2,016
  • 5
  • 23
  • 41
a verma
  • 25
  • 6
  • This error message means you use call a procedure as a function with return value. Only function can return values, not procedures. Probably you use a function without return one place here? If "Adder" is function - then you need to assign its return value to a variable. – Timur Samkharadze May 11 '18 at 12:58
  • @TimurSamkharadze can you please guide me how to call Ada Dll function i am new to Ada programming that's why not able to figure it out. – a verma May 11 '18 at 13:02
  • Error here is not about calling dll function. Is "Adder" function or procedure? If Adder is a function - then assign its return value to something. I did a mistake in first comment - not procedure as function, but function as procedure. – Timur Samkharadze May 11 '18 at 13:04
  • ok got it but now getting linking error – a verma May 11 '18 at 13:09
  • got this working thanks for help. – a verma May 11 '18 at 13:42
  • There's nothing about DLL's in the included code and commands used in your question. Please add the missing parts, so the rest of us can see what you've attempted to do. – Jacob Sparre Andersen May 11 '18 at 16:43
  • @JacobSparreAndersen already resolved it thanks anyway. if you can help me with my other question that would be great help. – a verma May 11 '18 at 16:45
  • https://stackoverflow.com/questions/50179640/i-am-trying-to-implement-a-pair-of-aws-servers-that-use-ssl-to-create-self-signe?noredirect=1#comment87403753_50179640 – a verma May 11 '18 at 16:50
  • 1
    @arjitaverma I'll be glad to help. But please remember that we are not only answering questions to help you, just as much for others who run into similar problems. - Since you've found an answer, please post it as an answer. There is nothing wrong in answering your own questions here. – Jacob Sparre Andersen May 11 '18 at 20:44
  • @JacobSparreAndersen yeah sure. – a verma May 14 '18 at 12:57
  • See [this example](https://github.com/Lucretia/sdlada/blob/master/test/libraries.adb) which binds to a C function to do it, but the it's the same if you bind to dlopen() or whatever the OS has. – Luke A. Guest May 21 '18 at 14:49
  • pragma Linker_Options ("Addition.dll"); -- Two points about this part. First, if Ada DLL is really Ada DLL (Convention => Ada everywhere), using library project in gprbuild is a preferred way to link. Second, if it's library supposed to be usable from another languages, it is preferred to write a proper .imp file for DLL imports. Write .imp that maps Your_DLL_Name_Function_Name compile-time symbol to Function_Name in DLL; compile .imp and reference this compiled file as opposed to .dll in pragma. Write Export aspects or pragmas with "Your_DLL_Name_Function_Name" as name to avoid collisions. – OCTAGRAM Jun 17 '18 at 20:05

3 Answers3

2

Are you using GNAT to compile and link your code? Essentially, you have to pass the necessary parameter for the library you are calling (you used the term "DLL", presumably meaning you are in a Microsoft Windows environment). I can't comment directly on Windows, but in Linux, you can pass -l. You can do that through the -largs argument so, for example, I have a situation where I am compiling an application that uses the wiringPi C library (on a Raspberry Pi) and what I pass is "-largs -lwiringPi". I have a separate package that provides an interface to that library. I guess your "Addition" package does that, if not, we need to see what it is.

Another technique, more generally for using libraries, is to use the tools provided by GNAT. For example, if "Addition" was a part of the GNAT tools libraries, then you could use the method. In this case, you have a separate project file (or, more to the point, Gnat does), that you reference in your project file. I presume you are using project files? A good reference is here: https://learn.adacore.com/courses/GNAT_Toolchain_Intro/chapters/gprbuild.html?highlight=project That also shows how you might create your own tool sets.

For one of my projects (I will pop it on GitHub when I have all the bugs fully ironed out), I have a Makefile that looks like:

#########################################################
#             Make file for Light Switches              #
#########################################################

# Use standard variables to define compile and link flags
ACC=gprbuild
TA=light_switches
TS=$(TA).gpr
HOST_TYPE := $(shell uname -m)
ifeq ($(HOST_TYPE),amd)
    TARGET=sparc
else ifeq ($(HOST_TYPE),x86_64)
    TARGET=amd64
else ifeq ($(HOST_TYPE),x86)
    TARGET=x86
else ifeq ($(HOST_TYPE),i686)
    TARGET=x86
else ifeq ($(HOST_TYPE),arm)
    TARGET=pi
else ifeq ($(HOST_TYPE),armv7l)
    TARGET=pi
endif
BIN=/usr/local/bin
ETC=/usr/local/etc
VAR=/var/local
TD=obj_$(TARGET)
ifeq ("$1.",".")
    FLAGS=-Xhware=$(TARGET)
else
    FLAGS=-Xhware=$(TARGET) $1
endif
ifeq ($(TARGET),pi)
    FLAGS+=-largs -lwiringPi
endif

lightswitches:
    $(ACC) -P $(TS) $(FLAGS)

# Define the target "all"
all:
    lightswitches:

# Clean up to force the next compilation to be everything
clean:
    gprclean -P $(TS)

dist-clean: distclean

distclean: clean

install:
    cp $(TD)/$(TA) $(BIN)
    cp $(TD)/$(TA).xml $(VAR)
    cp $(TD)/$(TA).xsd $(ETC)
    cp $(TD)/$(TA).rc $(ETC)/init.d/$(TA)
    cp $(TD)/$(TA).default $(ETC)/default/$(TA)

Then I have a project file that looks like:

with "xmlada";
with "adasockets";
with "../tools/dstrings";
project Light_Switches is

   type Hware_Option is ("sparc", "amd64", "x86", "pi");
   Hware : Hware_Option := external ("hware", "amd64");

   for Languages use ("ada");
   case Hware is
   when "pi" =>
      for Source_Dirs use ("src/", "src/pi/", "../tools/");
   when others =>
      for Source_Dirs use ("src/", "src/non_pi/", "../tools/");
   end case;
   for Main use ("light_switches.adb");
   --  Using "hware" variable for obj directory
   for Object_Dir use "obj_" & hware & "/";

   package Ide is
      for Documentation_Dir use "doc/";
   end Ide;

   for Source_Files use ("error_log.adb",
      "error_log.ads", "general_storage_pool.adb", "general_storage_pool.ads",
      "switch_types.ads", "switch_database.ads", "switch_database.adb", 
      "lights_control.ads", "lights_control.adb", "light_switch_version.ads", 
      "light_switch_functions.ads", "light_switch_functions.adb", 
      "basic_lights_interface.ads", "basic_lights_interface.adb", 
      "host_functions.ads", "host_functions.adb", "host_functions_thin.ads", 
      "generic_command_parameters.ads", "generic_command_parameters.adb", 
      "interlocks.ads", "interlocks.adb",
      "calendar_extensions.adb", "calendar_extensions.ads", 
      "dynamic_lists.adb", "dynamic_lists.ads", 
      "string_conversions.ads", "string_conversions.adb",
      "wiring_pi.ads", "wiring_pi.adb",
      "generic_versions.ads", "generic_versions.adb", "light_switches.adb");

end Light_Switches;

The projects that this project uses are at the top in the with statements. You might notice that one of them is one of mine (in my "tools" directory). Also, in the above, wiring_pi.adb is in two formats, one being in the "pi" directory and the other in the "non_pi" directory. The one in the non_pi directory is a stub (for testing on non-Pi hardware) and the one in the pi directory has the list of routines that exist in the wiringPi C library file (which is a .so file in the unix world, equivalent to a DLL).

I hope that helps. If not, we would be happy to entertain once we have some more information about your full set of files in your project.

1
with Ada.Text_IO;

with Addition;

procedure Loader is
   use Ada.Text_IO;
   use Addition;

   I : Integer;
begin
   I := Adder (4, 3);
   Put (Integer'Image (I));
end Loader;
Jacob Sparre Andersen
  • 6,733
  • 17
  • 22
a verma
  • 25
  • 6
  • 1
    Thank you for this code snippet, which might provide some limited, immediate help. A [proper explanation](https://meta.stackexchange.com/q/114762) would greatly improve its long-term value by showing why this is a good solution to the problem and would make it more useful to future readers with other, similar questions. Please [edit](https://meta.stackoverflow.com/posts/360251/edit) your answer to add some explanation, including the assumptions you’ve made. – Maximilian Peters May 14 '18 at 13:13
  • 1
    It would be nice if you could add the source for the package Addition as well as the project file and your build command(s). – Jacob Sparre Andersen May 14 '18 at 17:32
0

I don really now much about the calling but you can link dll to the project. example...

with Ada.Text_IO;   use Ada.Text_IO;

with Interfaces;    use Interfaces;

with Interfaces.C;  use Interfaces.C;

with System;        use System;
 
with Ada.Unchecked_Conversion;



procedure Shared_Library_Call is

 -- Interface to kernel32.dll which is responsible for loading DLLs under Windows.
  
 -- There are ready to use Win32 bindings. We don't want to use them here.
   --

   type HANDLE is new Unsigned_32; 

-- on x64 system, replace by Unsigned_64 to make it work

   function LoadLibrary (lpFileName : char_array) return HANDLE;

   pragma Import (stdcall, LoadLibrary, "LoadLibrary", "_LoadLibraryA"); 

-- Ada95 does not have the @n suffix.
 
   function GetProcAddress (hModule : HANDLE; lpProcName : char_array)
      
return Address;

   pragma Import (std call, GetProcAddress, "GetProcAddress",

"_GetProcAddress"); --

   --
   -- The interface of the function we want to call. It is a pointer (access type)

   -- because we will link it dynamically. The function is from User32.dll
  
 --
   type MessageBox is access function 
        (  hWnd      : Address     := Null_Address;
           lpText    : char_array;
           lpCaption : char_array  := To_C ("Greeting");
           uType     : Unsigned_16 := 0
        )  return Integer_16;


   pragma Convention (Stdcall, MessageBox);

   function To_MessageBox is new Ada.Unchecked_Conversion (Address, MessageBox);
 
   **

**Library : HANDLE := LoadLibrary (To_C ("user32.dll"));

   Pointer : Address := GetProcAddress (Library, To_C ("MessageBoxA"));**

**

begin

   if Pointer /= Null_Address then

      declare

         Result : Integer_16;

      begin

         Result := To_MessageBox (Pointer) (lpText => To_C ("Hello!"));

      end;

   else

      Put_Line ("Unable to load the library " & HANDLE'Image (Library));

   end if;

end Shared_Library_Call;