Using C# Dll in Fortran

Using C# Dll in Fortran

Hello,
 
I created following Code in C#: (I used R.Giesecke Dllexport Template to create a unmanaged Dll with C#... it creates me a ".lib" and ".dll" file with (managed) C# )

//UnmanagedExports.cs

using System;
using System.Collections.Generic;
using System.Text;
using RGiesecke.DllExport;

namespace AddDll
{
    class MyAddDll
    {
        [DllExport("Add", CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
        public static int Add(int a, int b)
        {
            return a + b;
        }
    }
}

The C# Dll Code runs well without errors and creats me a AddDll.lib and AddDll.dll file.

I would like to import/use that DLL in Fortran. I am not familar with Fortran. My Code:

program CallAdd
  !DEC$ATTRIBUTE DLLIMPORT :: AddDll
  implicit none
  Integer a, b
  a = 4
  b = 3
  Print*, 'a + b =', Add(a, b)
end program CallAdd

I've got following errors:

Error    3     error #6404: This name does not have a type, and must have an explicit type.   [ADD]
Error    4    Compilation Aborted (code 1)   

and one Warning:

Warning    1    Platform is AnyCpu, creating binaries for each CPU platform in a separate subfolder...

Can anyone help me to eliminate the errors?

Thx in advance!

 

 

10 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

Assuming that your dll is called 'ADDDLL.DLL', then it should contain the symbol 'Add' for the function Add that you define.

The accompanying file 'ADDDLL.LIB' will contain the details of the exported symbol 'Add' (note the mixed case name). You must include the file ADDDLL.LIB into your Fortran project so that the program can link with the dll file ADDDLL.DLL and find the symbol 'Add'.

There are a couple of other things you must do as well. You must make sure that the Fortran program executable can find the DLL, so best to first copy it to the folder containing your Fortran executable. You must also change your compiler directive DLLIMPORT to refer EXACTLY  to the symbol that you exported from the DLL, so change it to DLLIMPORT, ALIAS :: 'Add': Add so that the linker will look for the symbol having exactly the same mixed case name 'Add' that is defined by the C# code, other wise, by default, the Fortran compiler always defaults external symbols to UPPER CASE and so would by default be looking for 'ADD' which is not the same as 'Add' and so it would not be found during linking.

Alternatively, you could avoid having to add the latter wrinkle by simply renaming your C# function 'ADD' and exporting the name as upper-case, in which case it will be in the default case recognised by Fortran. and you would not need the ALIAS directive.

Well, your compilation failure is because you have not told Fortran anything about the routine named "Add".

Try replacing the slightly-incorrect "dllimport" statement with this clause:

interface
        integer function Add (I, J) bind(c, name="Add")
        integer, value :: I, j
        !dec$ attributes dllimport :: Add
        end function Add
end interface

This is sort-of like declaring a function prototype in C, and further says that the external routine is a C routine (close enough in this case).  It also outputs the "dllimport" request for the name {hopefully} exported by the C# dll.

There is a huge problem here though, and that's the STDCALL declaration in the C# declaration.   Are you running on IA32 or Intel64?   If it's Intel64, then no problem.  If it's IA32 then ... my interface block above won't work and I'll have to provide you something non-standard.   We've improved our use of STDCALL with BIND(c) in a release available later this year, but that won't help you now of course.

Let me know about the hardware -

                  --Lorri

 

PS:  I suppose this is where I should admit that while I'm fluent in the Fortran<->C interoperability, I'm not so fluent in C#, so hopefully this is All Good.

 

 

Quote:

Lorri Menard (Intel) wrote:
   We've improved our use of STDCALL with BIND(c) in a release available later this year, but that won't help you now of course.

 

You have! Excellent :-) I have been winging about this for quite some time. What are you doing, adding a stdcall attribute to the bind(c)?

We're no longer disallowing the !DEC$ ATTRIBUTE STDCALL on the bind(c) routine.

In other words, you'll be able to write this:

subroutine foo (i) bind(c)
!dec$ attributes stdcall :: foo
integer i
end subroutine foo

It won't change the passing mechanism for "I" though; it will still be pass-by-reference unless you explicitly say VALUE.

However, the external name and stack-cleanup mechanism will be stdcall-compliant.

                    --Lorri

 

Quote:

Lorri Menard (Intel) wrote:

We're no longer disallowing the !DEC$ ATTRIBUTE STDCALL on the bind(c) routine.

In other words, you'll be able to write this:

subroutine foo (i) bind(c)
!dec$ attributes stdcall :: foo
integer i
end subroutine foo

It won't change the passing mechanism for "I" though; it will still be pass-by-reference unless you explicitly say VALUE.

However, the external name and stack-cleanup mechanism will be stdcall-compliant.

                    --Lorri

 

I guess that is a reasonably simple change to make. I assume we also to not need the decorate attribute to make x32/x64 work? That all being the case the interface drops to having one non-standard !dec$ line per interface rather than potentially several which is as good as it gets anyway since the fortran standard does not give an option any option for the stdcall interface.

Hello,

sorry the remote desktop of my university was down. So I was unable to work further.

@ Lorri: Thanks for your reply. I am running with IA-32. Furthermore I can change the declaration in C# to "Cdecl" if it will help!?

@Anthony: Thanks for the advices! I followed the instructions. The DLL must be linked correctly (I hope).

I changed my Fortran Code to the following:

MODULE MYEXTERNALS
USE iso_c_binding
INTERFACE
FUNCTION AddDll(a,b) RESULT(ret) bind(c, name="AddDll")   
USE, intrinsic :: iso_c_binding
INTEGER(c_int), VALUE, intent(in) :: a,b
INTEGER(c_int) :: ret
END FUNCTION
END INTERFACE
END MODULE MYEXTERNALS


PROGRAM CallAdd
!DEC$ATTRIBUTE DLLIMPORT, ALIAS :: 'Add': Add
USE MYEXTERNALS
IMPLICIT NONE
INTEGER a, b
a = 4
b = 3
PRINT*, 'a + b =', Add(a, b)
END PROGRAM CallAdd

I get the following errors:

error #6404: This name does not have a type, and must have an explicit type.   [ADD]

Compilation Aborted (code 1)  

and one message:

remark #5140: Invalid CDEC$ directive

Best Reply

As I mentioned in an earlier post, you have exported the mixed-case name 'Add' from your C# dll, so you must use EXACTLY the same name in your Fortran. So change your compiler directive to

!DEC$ATTRIBUTE DLLIMPORT, ALIAS :: 'Add': Add

The compiler objects to you not defining a variable or function 'Add' in your Main program, and you have not even defined anything called 'Add' in your EXTERNALS module either. Perhaps you should change the EXTERNALS function interface to

FUNCTION Add(a,b) RESULT(ret) bind(c, name="Add")

so that it mentions the actual mixed-case name that the C# exports, and also perhaps you might try adding the DLLIMPORT directive to the FUNCTION interface.

It works!!!

Thanks a lot Anthony! The next attempt will be to do this with arrays...

So here for others the final working code:

C# "unmanaged" - DLL with R. Giesecke Template: https://www.nuget.org/packages/UnmanagedExports

using System;
using System.Collections.Generic;
using System.Text;
using RGiesecke.DllExport;

namespace AddDll
{
    class MyAddDll
    {
        [DllExport("Add", CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
        public static int Add(int a, int b)
        {
            return a + b;
        }
    }
}

Fortran Code:

MODULE MYEXTERNALS
USE iso_c_binding
INTERFACE
FUNCTION Add(a,b) RESULT(ret) bind(c, name="Add")
USE, intrinsic :: iso_c_binding
INTEGER(c_int), VALUE, intent(in) :: a,b
INTEGER(c_int) :: ret
END FUNCTION
END INTERFACE
END MODULE MYEXTERNALS


PROGRAM CallAdd
!DEC$ATTRIBUTE DLLIMPORT, ALIAS :: 'Add': Add
USE MYEXTERNALS
IMPLICIT NONE
INTEGER a, b
a = 4
b = 3
PRINT*, 'a + b =', Add(a, b)
PAUSE
END PROGRAM CallAdd

 

 

 

I think there is still a tiny bit of confusion ...

You don't need to mention "AddDLL" anywhere in your program.

It's true that it's the C# namespace, and I assume it's the name of the actual DLL, but really, that name is not interesting to Fortran.

What *is* interesting to Fortran is the routine you want to call, which is named Add.

As Anthony suggested, edit your "myexternals" module to reference "Add" instead of "AddDLL" , and take the "alias" clause off your dllimport statement.

And, yes, CDECL should be OK, since that's the default calling standard we use for Fortran.

Finally - at link time you do need to include "adddll.lib" in  your link line, otherwise your "Add" routine won't be found.

               --Lorri

 

 

Login to leave a comment.