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

Passing a string to Fortran

Bradley_P_1
Beginner
5,029 Views

I've had quite a lot of luck interoperating between Fortran and C# using Robert Giesecke​'s "Unmanaged Exports" library.  However, one of the things I haven't been able to achieve yet is sending a string from C# to Fortran.  The worst of it is that I get no indication of what's wrong.  My Fortran application just crashes.  From all the research I've done, it would seem to me the following should work:

C# "sender"

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyStruct
{
    [MarshalAs(UnmanagedType.LPStr, SizeConst = 200)]
    public string Title;
    public int SourceNode;
    public int EndNode;
}

Fortran "receiver"

type, bind(c) :: MyFirstStruct
    character(200, kind=C_CHAR) :: Title
    integer :: SourceNode
    integer :: EndNode
end type

When I run this I get the following:

forrtl: severe (172): Program Exception - exception code = 0x4352 (17234)

Any help or ideas would be much appreciated!

Brad.

P.S.  I can include the entire source for both sides, if it helps - just let me know.

0 Kudos
1 Solution
GVautier
New Contributor II
5,003 Views

Hello

I agree about the fact that if it works from C to C# it will works from Fortran

I think you should go back to basis.

First, replace your function with a subroutine because Fortran creates a shadow argument for function returning structures a strings.

subroutine GetStruct(struct) bind(C, name="GetStruct")
	import MyFortranStruct
	type(MyFortranStruct) :: struct
!DEC$ ATTRIBUTES REFERENCE :: struct 
end subroutine 

and on C# side the equivalent of :

void GetStruct(MyFortranStruct *result)
   {
   string="This is the title, padded to 200 characters just to make sure 345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
   strncpy(&(result->Title),string,min(len(string),200));
   result->SourceNode = 123;
   result->EndNode = 321;
   return;
   }

2nd, on both sides (before the call to GetStruct in Fortran and in GetStruct function in C#), print the size and address of the structure and addresses of all members. They must match.

3nd, find a way in C# function GetStruct to fill an array of chars (because that's what is a Fortran string)

View solution in original post

0 Kudos
24 Replies
FortranFan
Honored Contributor III
3,900 Views

@Bradley P.,

Try this on the C# side:

public const Int32 LENSTRING = 200;

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = LENSTRING)]
    public char[] Title;
    public int SourceNode;
    public int EndNode;
}

Note using BIND(C), Fortran character variable can be made compatible with a companion processor that handles the type as C char; the above struct with char[] component tries to emulate that with the Marshaling directives; note .NET 'string' class is a different type.

Not a big deal, but on the Fortran side, you may want to mark your integer types as [fortran] integer(kind=c_int) :: ... [/fortran] a

 

0 Kudos
Bradley_P_1
Beginner
3,900 Views

FortranFan wrote:

Try this on the C# side:

Thank a lot for your suggestion.  Unfortunately, that was one of the many things I've already tried, and it fails with the same meaningless result:

forrtl: severe (172): Program Exception - exception code = 0x4352 (17234)

It's enough to make me think I'm doing something else wrong, but I can't figure out what.

Brad.

0 Kudos
FortranFan
Honored Contributor III
3,900 Views

Bradley P. wrote:

It's enough to make me think I'm doing something else wrong, but I can't figure out what. ..

You may need to show your actual code and share the details, the readers then may be able to help you.

module m
   
   use, intrinsic :: iso_c_binding, only : c_char, c_int, c_null_char, c_loc, c_f_pointer

   implicit none

   private

   integer(c_int), parameter :: LENSTRING = 200

   type, bind(C), public :: MyStruct
      character(kind=c_char,len=1) :: Title(LENSTRING)
      integer(c_int)               :: SourceNode
      integer(c_int)               :: EndNode
   end type MyStruct
   
   public :: F_sub

contains

   subroutine F_sub( struct ) bind(C, name="F_sub")

      implicit none

      !.. Argument list
      type(MyStruct), intent(inout) :: struct

      call load_title( struct%Title )
      struct%SourceNode = 42
      struct%EndNode = 99
      
      return
      
   contains
   
      subroutine load_title( title ) bind(C)
      
         character(kind=c_char,len=1), target :: title(LENSTRING)
         
         !.. Local variables
         character(kind=c_char,len=LENSTRING), pointer :: F_title
            
         call c_f_pointer( c_loc(title), F_title )
         F_title = "Hello World!"
            
         F_title => null()
            
         return
      
      end subroutine load_title

   end subroutine F_sub

end module m
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

namespace TestStruct
{
    static class For
    {

        internal const Int32 LENSTRING = 200;

        [StructLayout(LayoutKind.Sequential)]
        internal struct MyStruct
        {
           [MarshalAs(UnmanagedType.ByValArray, SizeConst = LENSTRING)]
           internal char[] Title;
           internal int SourceNode;
           internal int EndNode;
        }

        [DllImport("For.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern void F_sub(ref MyStruct s);

    }

    static class PassStruct
    {
        static void Main()
        {

           For.MyStruct foo;

           char[] tmpchar = new char[For.LENSTRING];

           foo = new For.MyStruct();
           foo.Title = new char[For.LENSTRING];
           foo.SourceNode = 0;
           foo.EndNode = 0;

           try 
           {

               Console.WriteLine("\n" + " --- Test Pass Struct with char[] to Fortran ---" + "\n");

               // Call Fortran procedure
               For.F_sub(ref foo);

               Console.WriteLine(new string(foo.Title, 0, For.LENSTRING) + "\n");
               Console.WriteLine("SourceNode = " + foo.SourceNode.ToString() + "\n");
               Console.WriteLine("EndNode = " + foo.EndNode.ToString() + "\n");

            } 
            catch (Exception ex) 
            { 
                Console.WriteLine(ex.Message); 
            } 

        }
    }
}

Upon execution,

 --- Test Pass Struct with char[] to Fortran ---

Hello World!



SourceNode = 42

EndNode = 99

 

0 Kudos
Bradley_P_1
Beginner
3,900 Views

FortranFan wrote:

You may need to show your actual code and share the details, the readers then may be able to help you.

Whereas the code you showed looks good, it's sending information in the wrong direction.  I'm trying to get a structure INTO a Fortran executable FROM a C# DLL.  Yes, it's probably the reverse of what most people attempt.  :-)

Here is my source.  First the C# DLL:

using RGiesecke.DllExport;
using System.Runtime.InteropServices;

namespace CSharpDll
{
    public class Structures
    {
        [DllExport("GetInt", CallingConvention = CallingConvention.Cdecl)]
        public static int GetInt()
        {
            return 123;
        }

        public const int LENSTRING = 200;

        [StructLayout(LayoutKind.Sequential)]
        public struct MyFortranStruct
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = LENSTRING)]
            public char[] Title;
            public int SourceNode;
            public int EndNode;
        }

        [DllExport("GetStruct", CallingConvention = CallingConvention.Cdecl)]
        public static MyFortranStruct GetStruct()
        {
            var result = new MyFortranStruct();
            result.Title = "This is the title, padded to 200 characters just to make sure 345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890".ToCharArray();
            result.SourceNode = 123;
            result.EndNode = 321;

            return result;
        }
    }
}

Next the Fortran module I called "Interfaces":

    module InterfacesModule
    use iso_c_binding
    implicit none

    type, bind(c) :: MyFortranStruct
        character(200, kind=C_CHAR) :: Title
        integer :: SourceNode
        integer :: EndNode
    end type

    interface

    function GetInt() bind(C, name="GetInt")
    integer :: GetInt
    end function

    function GetStruct() bind(C, name="GetStruct")
    import MyFortranStruct
    type(MyFortranStruct) :: GetStruct
    end function

    end interface
    end module

Finally the Fortran executable:

    program FortranStructures
    use InterfacesModule

    implicit none

    ! Variables
    type(MyFortranStruct) :: tmpStruct

    ! Body of FortranStructures
    print *, 'Before...'
    
    print *, GetInt()
    
    tmpStruct = GetStruct()
    print *, tmpStruct%Title
    print *, tmpStruct%SourceNode
    print *, tmpStruct%EndNode
    
    print *, 'After...'

    end program FortranStructures

I've attached a ZIP file that will hopefully be available to any and all.  It contains all the source to demonstrate the problem.  I'm running Visual Studio Professional 2015 with "Intel Parallel Studio XE 2016 Update 3 Composer Edition for Fortran Windows​".  With those two packages installed, the user should be able to open and run the solution ("FortranStructures.sln") contained in the ZIP.  The solution consists of two projects:  "CSharpDll.csproj" and "FortranStructures.vfproj​".  Note that the C# dll requires the "UnmnagedExports" NuGet that I referred to earlier.

Thanks again!

Brad.

0 Kudos
GVautier
New Contributor II
3,900 Views

Hello

I will have declared Title in MyFortranstruct on C side as :

public char Title[200];

The two structures must have the same size and members relative address must be equal on both sides and next, I will have copied the string explicitly:
 

for( i=0 ; i<200 ; i++}
    {
    if( i < strlen(string) )
        result.Title=string;
   else
       result.Title=" "
    }

 

0 Kudos
Bradley_P_1
Beginner
3,900 Views

gvautier wrote:

I will have declared Title in MyFortranstruct on C side as :

public char Title[200];

I understand what you're saying, but this is C#, not C nor C++.  In C#, the array size cannot be specified in a variable declaration.

Brad.

0 Kudos
Anthony_Richards
New Contributor I
3,900 Views

Have you tried sending two integer values instead: i.e the address of the first character in the string and the total length of the string?

You need to be aware whether or not the string is stored on the C# side as 'wide' characters i.e. with hex'00' added between each character and terminated with a double hex'00'. If that is the case, on the Fortran side you will need to use a windows API for WideCharacters to change from widecharacter string to a normal character string in order to extract the string you want.

0 Kudos
Bradley_P_1
Beginner
3,900 Views

Anthony Richards wrote:

Have you tried sending two integer values instead: i.e the address of the first character in the string and the total length of the string?

Perhaps you could show me how?

Brad.

0 Kudos
GVautier
New Contributor II
3,900 Views

Bradley P. wrote:

Quote:

gvautier wrote:

 

I will have declared Title in MyFortranstruct on C side as :

public char Title[200];

 

 

I understand what you're saying, but this is C#, not C nor C++.  In C#, the array size cannot be specified in a variable declaration.

Brad.

What I want to say is that you must be sure that the structures on both sides have the same length. Check it by printing the lengths and offsets of members on Fortran and C# side. It must be 204 or 208 depending on alignment and integer size on Fortran side. Then try to put only one character back to Fortran.

In the final version, I think that it will be preferable to pad the Fortran string to the total length with space.

 

 

0 Kudos
Anthony_Richards
New Contributor I
3,900 Views

See this post for how to solve one problem with C#-Fortran string transfers.

https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/371292

When, on the C# side you define, for example, a string called logfile which you use as an argument  in a C# function to be CALLED
by Fortran,

C# -  [MarshalAs(UnmanagedType.LPStr] string logfile

On the Fortran side, you should use a compiler directive to apply the REFERENCE attribute to the argument of the called function because in this case C# sends a pointer (i.e. an address) to the string.

Note that what works OK above uses a SUBROUTINE call from FORTRAN to transfer a string from C# to Fortran. I note you use a FUNCTION . So perhaps try the subroutine method instead.

 

0 Kudos
Bradley_P_1
Beginner
3,900 Views

gvautier wrote:

What I want to say is that you must be sure that the structures on both sides have the same length. Check it by printing the lengths and offsets of members on Fortran and C# side. It must be 204 or 208 depending on alignment and integer size on Fortran side. Then try to put only one character back to Fortran.

I've tried all manner of length matching.  I doubt there's something there that I haven't tried.  However, if you can get the sample I posted working, I'd be delighted!

Brad.

0 Kudos
Bradley_P_1
Beginner
3,900 Views

Anthony Richards wrote:

See this post for how to solve one problem with C#-Fortran string transfers.

Again, from everything I read in that post, it's passing a string in the other direction (i.e. from Fortran to C#).  I have that direction working like a charm.

I'm wondering if I could get you to try the sample I uploaded.  Getting that working would be the quickest way to 1) understand my question, and 2)  know whether my question is answered.

Thanks!

Brad.

0 Kudos
Jon_D
New Contributor I
3,900 Views

Bradley,

I have had success with StringBuilder when passing a string from C# to Fortran if Fortran has to manipulate the string and pass it back to C#. The only thing is your StringBuilder variable must have enough capacity to hold the string generated within Fortran. If string manipulation is not needed, then String type works as well.

Example for non-manipulated string:

Fortran:

  FUNCTION IW_SetLogFile(iLen,cFileName) RESULT(iStat)
    INTEGER,INTENT(IN)             :: iLen
    CHARACTER(LEN=iLen),INTENT(IN) :: cFileName
    INTEGER                        :: iStat
  END FUNCTION IW_SetLogFile

C#:

   public static extern int IW_SetLogFile(ref int iLen, string cFileName);

Example for string manipulated in Fortran:

Fortran:

  SUBROUTINE IW_GetName(iLen,cName)
    INTEGER,INTENT(IN)              :: iLen
    CHARACTER(LEN=iLen),INTENT(OUT) :: cName
  END SUBROUTINE IW_GetName

C#:

   public static extern int IW_GetName(ref int iLen, StringBuilder cName);

On the C# side, iLen must be equal to the capacity of cName plus 1 (cName.Capacity+1).

Jon

 

0 Kudos
Bradley_P_1
Beginner
3,900 Views

JonD wrote:

I have had success with StringBuilder when passing a string from C# to Fortran if Fortran has to manipulate the string and pass it back to C#.

From what I can tell, the examples you provide still show only a C# caller.  I need Fortran to be the "caller" (i.e. the application).

Can I just ask that you try the sample I uploaded and see if you can get it working?

Thanks!

Brad.

0 Kudos
FortranFan
Honored Contributor III
3,900 Views

Bradley P. wrote:

..  I'm trying to get a structure INTO a Fortran executable FROM a C# DLL.  Yes, it's probably the reverse of what most people attempt.  :-)

..  Note that the C# dll requires the "UnmnagedExports" NuGet that I referred to earlier. ..

Brad,

I suggest a little homework: forget about Fortran briefly.  Since the direction you want to take is rather uncommon and it appears to depend entirely on what is possible with Robert Giesecke's "UnmnagedExports" NuGet - which can effectively be viewed as a middleman, a 3rd party solution - I suggest you try a C main program that calls your C# DLL, that is prove to yourself you are able exchange data in a C struct with the 3 components, especially a char[] array, with your C# dll.  You may then post it here.

If you are able to get a simple, standard C program to do what you want, you will be able to achieve the same with Fortran.  But if your C attempt is itself unsuccessful, you would need to work with Robert Giesecke or whoever supports NuGet to first get that to work.  You can then come back to Fortran.

But if I had a need such as yours, I would NOT use Robert Giesecke's "UnmnagedExports" NuGet system, not that I know anything or have anything against it.  It is just that I will be rather keen to avoid any further middlemen between Microsoft's .NET "managed code" framework and standard Fortran.  That is, BESIDES anything that Microsoft itself might recommend.  Looking at MSDN, that seems to be C++ code that can make use of COM interoperability in the Microsoft .NET Framework and can instantiate and free COM clients, all Microsoft components that are built and setup with Microsoft's own toolchain in Visual Studio.  Thus I would seek to introduce a THIN layer of C++ unmanaged, wrapper dll that will have 'extern C' methods callable by Fortran and the C++ dll, in turn, will communicate with C# DLL using Microsoft recommended approach e.g., https://support.microsoft.com/en-us/kb/828736#bookmark-4.

 

 

0 Kudos
Bradley_P_1
Beginner
3,900 Views

FortranFan wrote:

I suggest a little homework: forget about Fortran briefly.

I understand what you're saying, but my response is as follows:

  • ​It feels like I'm really close given what I've already been able to do.
  • I'm intentionally trying to avoid COM and the overhead and complexity it brings.
  • My final goal is to interoperate between C# and Fortran.  Given how close this feels, it seems a diversion to attempt to prove it first in C/C++ and then have to do the same again when I want to make it work with Fortran, especially when my C/C++ is as rusty as it is and I'd have to do a lot of learning there as well.
  • I'm still optimistic that the knowledge and expertise in this forum is sufficient to solve this problem.  I still think that there are people here who, if they took the time to download or recreate my simple sample, would be able to solve it very quickly or point me in the right direction.

​Brad.

0 Kudos
GVautier
New Contributor II
5,004 Views

Hello

I agree about the fact that if it works from C to C# it will works from Fortran

I think you should go back to basis.

First, replace your function with a subroutine because Fortran creates a shadow argument for function returning structures a strings.

subroutine GetStruct(struct) bind(C, name="GetStruct")
	import MyFortranStruct
	type(MyFortranStruct) :: struct
!DEC$ ATTRIBUTES REFERENCE :: struct 
end subroutine 

and on C# side the equivalent of :

void GetStruct(MyFortranStruct *result)
   {
   string="This is the title, padded to 200 characters just to make sure 345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
   strncpy(&(result->Title),string,min(len(string),200));
   result->SourceNode = 123;
   result->EndNode = 321;
   return;
   }

2nd, on both sides (before the call to GetStruct in Fortran and in GetStruct function in C#), print the size and address of the structure and addresses of all members. They must match.

3nd, find a way in C# function GetStruct to fill an array of chars (because that's what is a Fortran string)

0 Kudos
Anthony_Richards
New Contributor I
3,900 Views

There is some confusion here because in your first post you say you want to 'send' a string from C# to Fortran (implying C# control) whereas in one of your latest replies you state that Fortran is to be the caller (i.e. in control). You also insist that the Fortran is an application (executable) whereas the C# is a DLL.

Taking your initial statement that you want to 'send' a string from a memory location initiated/controlled within a C# DLL 'to' a Fortran application i.e. executable program. I think there are two interpretations of or scenarios for this process.

Scenario #1
This implies that the C# code has control at the point when the string 'transfer' is to take place. Thus, unless you are using Dynamic Data Exchange (which I believe you are not), when you want to give the Fortran program access to the C# string (the latter possibly created within the C# code), you will have to call, from within the C# DLL code (which has control), a function or subprogram existing within the Fortran program that would allow control to be switched to the Fortran program and simultaneously give it access either directly to the memory location of and the length of the string buffer in the C# DLL, or to a copy of the string and an integer containing its length in bytes. Let's call it the SendString function (in the Fortran program code).

If you want to then tweak the string in the Fortran program and return it eventually to C#, you will have to be very careful if you directly access the memory location of the string, as you must not risk writing before or beyond the buffer in which it is held. You can then call a C# subprogram which allows the C# DLL  to take back execution control (presumably until the DLL eventually returns control to the Fortran executable for it to complete to termination.)

After you have played with the string in the Fortran program (either directly, because you have its memory location, or indirectly because you have tweaked a copy of the string), then a call to GetString (a subprogram within the C# DLL) giving the address of the tweaked string and its buffer length or just allowing return to the C# code is required.

Scenario #2
This implies that the Fortran program has control at the point when the string 'transfer' is to take place. Thus, unless you are using Dynamic Data Exchange (which I believe you are not), when you want access to the already-existing C# string, you will have to call, from within the Fortran program, a function or subprogram existing within the C# DLL that would give you access either directly to the memory location of and the length of the string buffer, or to a copy of the string and an integer containing its length in bytes. Let's call it the GetString function (in the C# DLL).

If you want to then tweak the string in the Fortran program and return it eventually to C#, you will have to be very careful if you directly access the memory location of the string, as you must not risk writing before or beyond the buffer in which it is held. You can then call a C# subprogram which allows the C# DLL  to take over execution until the DLL eventually returns control to the Fortran executable for it to complete to termination.

After you have played with the string in the Fortran program (either directly, because you have its memory location, or indirectly because you have tweaked a copy of the string), then a call to SendString (a subprogram within the C# DLL) giving the address of the tweaked string and its buffer length or just allowing return to the C# code is required.

Note that changing the length of the string will require deallocation and allocation commands within the C# code in order to ensure maintenance of accurate string buffer length.

If scenario#1 is your wish, then it would appear that a Fortran subprogram name must be made available (i.e. 'imported') to the C# DLL code during its creation. This requires it to be exported from a DLL, as far as I can recall, so you are talking about a Fortran DLL being intermedate  between the Fortran executable and the C# DLL.

If scenario#2 is what is required, then the Fortran needs to be able to 'import' two C# DLL subprogram/function names during Fortran compliation (assuming that they can be exported in a form accessible to the Fortran compiler).

0 Kudos
Bradley_P_1
Beginner
3,900 Views

gvautier wrote:

First, replace your function with a subroutine because Fortran creates a shadow argument for function returning structures a strings.

THANK-YOU VERY MUCH!!!  This was the tip that finally got me to a working solution!

OK, as I said, I have a working solution.  Whereas I'd still appreciate comments on how this might be improved, it's working and I'm happy.  :-)

It is worth noting that, since the structure is created (i.e. allocated) in C#, this will likely be a memory leak.  In my case that really isn't much of a concern, since I'm passing a small structure and the Fortran application is just a straight run and done.  I might be able to fix this by creating the structure in Fortran first and passing it to the C# code and simply allowing the C# code to fill it in.  (I may even try that someday if/when time permits.)  However, for now I'm satisfied.

Here are the relevant parts.  My C# DLL consists of the following:

    public class Structures
    {
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct MyStruct
        {
            public int SourceNode;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 200)]
            public char[] Title;
            public int EndNode;
        }

        [DllExport("GetStruct", CallingConvention = CallingConvention.Cdecl)]
        public static void GetStruct(out MyStruct myStruct)
        {
            var result = new MyStruct();
            result.SourceNode = 123;
            result.Title = "This is Title #1".PadRight(200).ToCharArray();
            result.EndNode = 321;

            myStruct = result;
        }
    }

My Fortran interface looks like this:

    module InterfacesModule
    use iso_c_binding
    implicit none

    type, bind(c) :: MyStruct
        integer :: SourceNode
        character :: Title(200)
        integer :: EndNode
    end type

    interface

    subroutine GetStructSub(struct) bind(C, name="GetStruct")
    import MyStruct
    type(MyStruct) :: struct
    !DEC$ ATTRIBUTES REFERENCE :: struct
    end subroutine

    end interface
    end module

My Fortran application (calling program) looks like this:

    program FortranStructures
    use InterfacesModule

    implicit none

    type(MyStruct) :: tmpStruct

    print *, 'Before'
    
    call GetStructSub(tmpStruct)
    print *, tmpStruct%SourceNode
    print *, tmpStruct%Title
    print *, tmpStruct%EndNode
    
    print *, 'After'

    end program FortranStructures

Thanks again to all who contributed!

Brad.

0 Kudos
Bradley_P_1
Beginner
3,526 Views

Anthony Richards wrote:

There is some confusion here

Yes, I'm sorry.  Despite my attempts to be as specific as possible, I realized eventually that I still hadn't communicated my requirements as well as I should have.  That's one of the reasons I eventually posted my code - so that readers could see exactly what I was trying to do.  None-the-less, I now have a solution (see my other post), so I'm happy!  :-)

Brad.

0 Kudos
Reply