Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29269 Discussions

Is it possible to pass an object from Fortran 90/95/2003 to C#?

daninraleigh
Beginner
2,719 Views
I havea Fortran dll that is called from C#. Essentially, the Fortran dll creates some binary files that the C# app then reads. Now, instead of creating the binary files, it is desired to have the data passed directly to the C# app as some sort of object.Is this possible? Maybe if I modified the existing fortran code to be Fortran 2003 compliant? If it is possbile either with Fortran 90/95/2003 is there some documentation on how to do that?
0 Kudos
1 Solution
Steven_L_Intel1
Employee
2,719 Views
Ok. A TYPE is like a C struct.

You cannot use C# "new" to create an object of a Fortran type. What you could do is something like this:

[plain]module employee
use iso_c_binding

type, bind(C) :: employee_type
character(12) :: emplid
character(8) :: emplpermnum
character(50) :: empladdr
character(50) :: emplcity
character(50) :: emplstate
character(10) :: emplzip
end type employee_type

contains

function new_employee () bind(C)
type(C_PTR) :: new_employee
type(employee_type), pointer :: new_emp
allocate(new_emp)
! Fill in fields...
new_emp%emplid = ... something ...
...
! Convert Fortran pointer to C pointer
new_employee = c_loc(new_emp)
! Return pointer to new employee
return
end function new_employee

! Add get and put functions if needed
! Also add a "deallocate" routine if required

end module employee[/plain]

Since you're calling from C# and maybe Java, you'll need to put this into a DLL which will then require ATTRIBUTES DLLEXPORT directives for the routines, such as new_employee. The Fortran code may need to append C_NULL_CHAR to the trimmed value if the C# code wants NUL termination.

On the C# side, you'd have to declare what is essentially a C struct that has the same layout and declare new_employee as returning a pointer to that struct type.

I don't know if this meets your needs. I can tell you that a C# class is not something that Fortran can do anything with, and vice-versa.

View solution in original post

0 Kudos
15 Replies
onkelhotte
New Contributor II
2,719 Views
Quoting - daninraleigh
I havea Fortran dll that is called from C#. Essentially, the Fortran dll creates some binary files that the C# app then reads. Now, instead of creating the binary files, it is desired to have the data passed directly to the C# app as some sort of object.Is this possible? Maybe if I modified the existing fortran code to be Fortran 2003 compliant? If it is possbile either with Fortran 90/95/2003 is there some documentation on how to do that?

Search the forum, there are a few threads with discussion about passing data from a Fortran DLL to a C# project.

The only "difficulty" are character and array passing. In C# you need an object of stringbuilder class to receive a character value and arrays are stored the other way round (fortran[x,y] -> c#[y,x]).

Markus
0 Kudos
daninraleigh
Beginner
2,719 Views
Quoting - onkelhotte

Search the forum, there are a few threads with discussion about passing data from a Fortran DLL to a C# project.

The only "difficulty" are character and array passing. In C# you need an object of stringbuilder class to receive a character value and arrays are stored the other way round (fortran[x,y] -> c#[y,x]).

Markus


Thanks Markus, but I must not have been clear in my post. I am already passing data between Fortran and C#. However, rather than currently passing individual strings/characters/integers back and forth between C# and Fortran, I also want to now pass an entire object(what the contents of a binary file currently is from Fortran to C#). Has anyone done this? Is it possible? Can I do this in Fortran 2003 with OOP?
0 Kudos
Steven_L_Intel1
Employee
2,719 Views
The C interoperability features in Fortran 2003 are not "C++ interoperability". There is no notion of Fortran and C++ sharing a representation for "objects". So, you will have to pass data in a form that would be acceptable to C in order to do so in any kind of portable manner.
0 Kudos
daninraleigh
Beginner
2,719 Views
The C interoperability features in Fortran 2003 are not "C++ interoperability". There is no notion of Fortran and C++ sharing a representation for "objects". So, you will have to pass data in a form that would be acceptable to C in order to do so in any kind of portable manner.


Hello Lionel,
I was not asking about C interoperability. I figure since I can already pass Characters and Integers in the non-OOP Fortran, that hopefully with the somewhat OOP-Fortran2003 I could pass a data object. Are you saying this is not possible?
0 Kudos
Steven_L_Intel1
Employee
2,719 Views
Please clarify what you mean by "a data object". Can you show in "pseudo-code" what you'd like to do?

Fortran 2003's OOP features include the ability to have a variable of dynamic type. The dynamic type information is Fortran-specific and would not be understood by C#. Conversely, passing a dynamic type object from C# to Fortran would not be understood. It is possible to pass the actual data, but you must do so in a context where the type is fixed (this could be within a SELECT TYPE construct in Fortran, for example.)

I should note that Intel Fortran 11.0 does not support dynamic types. Version 11.1 will.
0 Kudos
daninraleigh
Beginner
2,719 Views
Please clarify what you mean by "a data object". Can you show in "pseudo-code" what you'd like to do?

Fortran 2003's OOP features include the ability to have a variable of dynamic type. The dynamic type information is Fortran-specific and would not be understood by C#. Conversely, passing a dynamic type object from C# to Fortran would not be understood. It is possible to pass the actual data, but you must do so in a context where the type is fixed (this could be within a SELECT TYPE construct in Fortran, for example.)

I should note that Intel Fortran 11.0 does not support dynamic types. Version 11.1 will.


Hi Lionel,
Thanks for continuing to help me on this. I think I can see now how my initial question is confusing.

What I would like to do is create a Fortran 2003 MODULE or maybe a TYPE???(not sure what these are yet)? Such as this that has all the values from a file:


MODULE EMPLOYEE
CHARACTER(LEN=12) :: EMPLID = IDFROMFILE
CHARACTER(LEN=8) :: EMPLPERMNUM = NUMBERFROMFILE
CHARACTER(LEN=50) :: EMPLADDR = ADDRFROMFILE
CHARACTER(LEN=50) :: EMPLCITY = CITYFROMFILE
CHARACTER(LEN=50) :: EMPLSTATE = STATEFROMFILE
CHARACTER(LEN=10) :: EMPLZIP = ZIPFROMFILE
END MODULE EMPLOYEE


In C# or Java I might do something like this:

public class Employee {

// data member variables
private string emplId;
private string emplPermNum;
private string emplAddr;
private string emplCity;
private string emplState;
private string emplZip;


//getters
public String getEmplId() {
return this.emplId;
}

...more getters/setters...


//Class constructor
public Employee(string emplId) {
this.emplId = emplId;
}


//methods that populate the data member variables
....

}




THEN in another C# Class, call the Fortran 2003 DLL's object/module/type and it's indiviual variable values


// this is how C# would instantiate the c# class object. Maybe C# calling the Fortran 2003 object/module/type would // be different
Employee emp = new Employee(3546289);


// then use the variables from the Employee class object/module/type
something = emp.EmplPermNum;
somethingElse = emp.EmplAddrr;
0 Kudos
Steven_L_Intel1
Employee
2,720 Views
Ok. A TYPE is like a C struct.

You cannot use C# "new" to create an object of a Fortran type. What you could do is something like this:

[plain]module employee
use iso_c_binding

type, bind(C) :: employee_type
character(12) :: emplid
character(8) :: emplpermnum
character(50) :: empladdr
character(50) :: emplcity
character(50) :: emplstate
character(10) :: emplzip
end type employee_type

contains

function new_employee () bind(C)
type(C_PTR) :: new_employee
type(employee_type), pointer :: new_emp
allocate(new_emp)
! Fill in fields...
new_emp%emplid = ... something ...
...
! Convert Fortran pointer to C pointer
new_employee = c_loc(new_emp)
! Return pointer to new employee
return
end function new_employee

! Add get and put functions if needed
! Also add a "deallocate" routine if required

end module employee[/plain]

Since you're calling from C# and maybe Java, you'll need to put this into a DLL which will then require ATTRIBUTES DLLEXPORT directives for the routines, such as new_employee. The Fortran code may need to append C_NULL_CHAR to the trimmed value if the C# code wants NUL termination.

On the C# side, you'd have to declare what is essentially a C struct that has the same layout and declare new_employee as returning a pointer to that struct type.

I don't know if this meets your needs. I can tell you that a C# class is not something that Fortran can do anything with, and vice-versa.
0 Kudos
daninraleigh
Beginner
2,719 Views
Ok. A TYPE is like a C struct.

You cannot use C# "new" to create an object of a Fortran type. What you could do is something like this:

[plain]module employee
use iso_c_binding

type, bind(C) :: employee_type
character(12) :: emplid
character(8) :: emplpermnum
character(50) :: empladdr
character(50) :: emplcity
character(50) :: emplstate
character(10) :: emplzip
end type employee_type

contains

function new_employee () bind(C)
type(C_PTR) :: new_employee
type(employee_type), pointer :: new_emp
allocate(new_emp)
! Fill in fields...
new_emp%emplid = ... something ...
...
! Convert Fortran pointer to C pointer
new_employee = c_loc(new_emp)
! Return pointer to new employee
return
end function new_employee

! Add get and put functions if needed
! Also add a "deallocate" routine if required

end module employee[/plain]

Since you're calling from C# and maybe Java, you'll need to put this into a DLL which will then require ATTRIBUTES DLLEXPORT directives for the routines, such as new_employee. The Fortran code may need to append C_NULL_CHAR to the trimmed value if the C# code wants NUL termination.

On the C# side, you'd have to declare what is essentially a C struct that has the same layout and declare new_employee as returning a pointer to that struct type.

I don't know if this meets your needs. I can tell you that a C# class is not something that Fortran can do anything with, and vice-versa.


Thanks Lionel,
I am completely new to pointers :-)

As a test I have put together a simple exe and dll based on the code below. I have tried the following in C# to create the pointer you spoke of. While trying to run I get an error message stating it cannot find the employee.dll. Which is odd as I have the dll employee.dll in the same direcotry as the test c# application. Am I creating and calling the pointer correctly to begin with?

[c-sharp]using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApp
{
    class Program
    {

        [DllImport("employee.dll", EntryPoint = "new_employee", CallingConvention = CallingConvention.StdCall)]
        private static extern void new_employee();

        [StructLayout(LayoutKind.Sequential)]
        public class EMPLOYEE_TYPE
        {

            public string strEmplid;
            public string strEmplpermnum;
            public string strEmpladdr;
            public string strEmplcity;
            public string strEmplstate;
            public string strEmplzip;
        }

        static void Main(string[] args)
        {

            EMPLOYEE_TYPE et = new EMPLOYEE_TYPE();
            et.strEmpladdr = new string('*', 50);

            new_employee();
            Console.WriteLine(et.strEmpladdr);

        }
    }
}[/c-sharp]



along with the following Fortran code



[cpp]!  employee.f90 
!
!  FUNCTIONS/SUBROUTINES exported from employee.dll:
!  employee - subroutine 
!
module employee
use iso_c_binding 

! Variables

! Body of employee
  
type, bind(C) :: employee_type 
    character(12) :: emplid 
    character(8)  :: emplpermnum 
    character(50) :: empladdr 
    character(50) :: emplcity 
    character(2)  :: emplstate 
    character(10) :: emplzip 
end type employee_type 
  
contains 
 
function new_employee() bind(C) 
    ! Expose function new_employee to users of this DLL
    
    !DEC$ ATTRIBUTES DLLEXPORT::new_employee
    type(C_PTR) :: new_employee 
    type(employee_type), pointer :: new_emp 
    allocate(new_emp) 
    
    ! Fill in fields... 
    new_emp%emplid = 'ABCDEFG'
    new_emp%emplpermnum = '123456'
    new_emp%empladdr = '789 main street'
    new_emp%emplcity = 'San Diego' 
    new_emp%emplstate = 'CA'
    new_emp%emplzip = '92124'
     
    ! Convert Fortran pointer to C pointer 
    new_employee = c_loc(new_emp) 
    ! Return pointer to new_employee 
    return 
end function new_employee 

end module employee[/cpp]

0 Kudos
Steven_L_Intel1
Employee
2,719 Views
FYI - my first name is Steve.

I see a couple of problems off the bat. You have specified STDCALL in the C# - does it allow "C" or some variant as the calling convention? If you must use STDCALL, then remove the BIND(C) from the "function new_employee" line and add !DEC$ ATTRIBUTES STDCALL :: new_employee.

Second is that the exported name may not match what you have in the C#, though if you drop the stdcall from the C# it should match up. That would be the preferable solution.

The C# application is going to look for the DLL in the current directory, whatever that is. Try putting in an explicit path for now and see if that works.
0 Kudos
daninraleigh
Beginner
2,719 Views
FYI - my first name is Steve.

I see a couple of problems off the bat. You have specified STDCALL in the C# - does it allow "C" or some variant as the calling convention? If you must use STDCALL, then remove the BIND(C) from the "function new_employee" line and add !DEC$ ATTRIBUTES STDCALL :: new_employee.

Second is that the exported name may not match what you have in the C#, though if you drop the stdcall from the C# it should match up. That would be the preferable solution.

The C# application is going to look for the DLL in the current directory, whatever that is. Try putting in an explicit path for now and see if that works.


Thanks Steve,
Honestly, my error message was: Unhandled Exception: System.DllNotFoundException: Unable to load DLL 'employee.dll': The specified module could not be found.

So, after adding libifcoremdd.dll and libmmd.dll I do not get the error anymore. However, I do not get any output :-(

Any ideas on how this could be changed?

I also changed the DllImport statement to this:
[c-sharp][DllImport("employee.dll", EntryPoint = "new_employee", CallingConvention = CallingConvention.Cdecl)]

so the class looks like this now:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApp
{
    class Program
    {

        [DllImport("employee.dll", EntryPoint = "new_employee", CallingConvention = CallingConvention.Cdecl)]
        private static extern void new_employee();

        [StructLayout(LayoutKind.Sequential)]
        public struct employee_type
        {
            public string strEmplid;            
            public string strEmplpermnum;            
            public string strEmpladdr;            
            public string strEmplcity;            
            public string strEmplstate;            
            public string strEmplzip;
            
        }

        static void Main(string[] args)
        {

            employee_type et = new employee_type();

            new_employee();
            
            Console.WriteLine(et.strEmplid);
            Console.WriteLine(et.strEmpladdr);
        }
    }
}
[/c-sharp]



0 Kudos
Steven_L_Intel1
Employee
2,719 Views
I think you missed the part where I said that the Fortran function returns a pointer to a "struct" with the new data. You just called the routine and discarded its return value. I did note that there is no interaction with the C# class object.

Perhaps I misunderstood what you wanted the Fortran code to do?
0 Kudos
daninraleigh
Beginner
2,719 Views
I think you missed the part where I said that the Fortran function returns a pointer to a "struct" with the new data. You just called the routine and discarded its return value. I did note that there is no interaction with the C# class object.

Perhaps I misunderstood what you wanted the Fortran code to do?


In the C# code I am trying to print out the inidivual values from the Fortran function new_employee. I have searched everywhere on how to get the pointer that is pointing to the struct/type in Fortran. Have also tried for C, and am running a little short apparently. I thought I was close :-(
0 Kudos
daninraleigh
Beginner
2,719 Views
To those that are interested, solution found with the following C# code:

[c-sharp]using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApp
{
    class Program
    {

        [DllImport("employee.dll", EntryPoint = "new_employee", CallingConvention = CallingConvention.Cdecl)]
        private static extern IntPtr new_employee();

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct employee_type
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
            public string strEmplid;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
            public string strEmplpermnum;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
            public string strEmpladdr;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
            public string strEmplcity;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
            public string strEmplstate;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
            public string strEmplzip;
            
        }

      
        static void Main(string[] args)
        {

            employee_type et = (employee_type)Marshal.PtrToStructure(new_employee(), typeof(employee_type));

            Console.WriteLine("Employee ID: "" + et.strEmplid + """);
            Console.WriteLine("Employee Permit Number: "" + et.strEmplpermnum + """);
            Console.WriteLine("Employee Address: "" + et.strEmpladdr + """);
            Console.WriteLine("Employee City: "" + et.strEmplcity + """);
            Console.WriteLine("Employee State: "" + et.strEmplstate + """);
            Console.WriteLine("Employee Zip: "" + et.strEmplzip + """);
            
        }
    }
}[/c-sharp]

0 Kudos
Steven_L_Intel1
Employee
2,719 Views
Yep - that's the ticket.
0 Kudos
craig_h
Beginner
2,719 Views
daninraleigh or Steve
I realize this is an old post but hopefully you'll see this and reply.

I've been trying to figure out how to pass strings inside a structure from C# to Fortran. I'm trying to pass them in a subroutine rather than the result of a function. Is this possible? Could your methodology be applied to that case?

I can get a stringbuilder class to pass outside of a structure and return values back but stringbuilders aren't allowed in a structures for some reason. Even if I could pass a structure with strings by value and not change them in Fortran that would probably be helpful although being able to change them would be ideal.

Also I would like to pass arrays in a structure and am still struggling with that as well. Any thoughts on how to do this or if it's possible?

Thanks a lot
0 Kudos
Reply