Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Beginner
60 Views

module data member interop with C#

Given this module: 
 

module FortranLibrary
    use Sample_Data
    
    implicit none
    
    real(8) :: A
    !DEC$ ATTRIBUTES DLLEXPORT :: A
    !DEC$ ATTRIBUTES ALIAS: 'A' :: A

    contains

    SUBROUTINE GetA(value)    
        !DEC$ ATTRIBUTES DLLEXPORT :: GetA
        !DEC$ ATTRIBUTES ALIAS: 'GetA' :: GetA
        !DEC$ ATTRIBUTES REFERENCE :: value
                    
        REAL(8), INTENT(OUT) :: value
        
        value = A
        
    END SUBROUTINE GetA
    
    subroutine DoWork()

        !DEC$ ATTRIBUTES DLLEXPORT :: DoWork
        !DEC$ ATTRIBUTES ALIAS: 'DoWork' :: DoWork

        A = 42

        return

    end subroutine DoWork

end module

How would I go about importing A into a C# application so that I can work with its reference directly? 

Calling the A function gives me an access violation exception and calling GetA seems to give a value rather than a reference.:

        #region Fortran Reference
        // Fortran DLL interface.
        [DllImport("FP1_Library.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void DoWork();
        [DllImport("FP1_Library.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void GetA(ref double val);
        //This is just a guess, since A is really data that is exported and not a function
        [DllImport("FP1_Library.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void A(out double val);

        #endregion

Is it even possible to import a data member directly? When I look at the dll using dependency walker I can see A listed as a function.

Is there some other approach that is better? I have tried passing the A member out through a subroutine but all I get is a value, not a reference.

Thanks!

Eric

0 Kudos
9 Replies
Highlighted
Black Belt Retired Employee
60 Views

I suggest you start reading at https://software.intel.com/en-us/fortran-compiler-developer-guide-and-reference-standard-tools-for-interoperability

Fortran has features for interoperability with C. To the extent that you can make other languages behave or reference like C, this should work for you.

0 Kudos
Highlighted
Honored Contributor I
60 Views

Bowman, Eric wrote:

.. Is it even possible to import a data member directly? When I look at the dll using dependency walker I can see A listed as a function.

Is there some other approach that is better? ..

Is the following what you are looking for?  Be careful though, because a direct reference implies getting a system pointer to "unmanaged" memory in .NET and that get can be unsafe.  If you can explain better your needs, readers might be able to provide you with optoins.

module m

   use, intrinsic :: iso_c_binding, only : c_double, c_ptr, c_loc

   implicit none

   private
   
   real(c_double), target :: a = 0.0_c_double

   public :: get_a
   public :: output_a

contains

   function get_a() result(r) bind(C, name="get_a")
      !dir$ attributes dllexport :: get_a

      ! Function result
      type(c_ptr) :: r
   
      r = c_loc(a)

   end function get_a

   subroutine output_a() bind(C, name="output_a")
      !dir$ attributes dllexport :: output_a

      print *, "out_a: a = ", a

   end subroutine output_a

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

namespace Fortran
{

   static class Fdll
   {

      [DllImport("Fdll.dll", CallingConvention = CallingConvention.Cdecl)]
      internal static extern IntPtr get_a();

      [DllImport("Fdll.dll", CallingConvention = CallingConvention.Cdecl)]
      internal static extern void output_a();

   }

   static class TestFortran
   {
      static void Main()
      {

         try
         {

            Console.WriteLine(" --- Test Fortran Data Access ---");

            // Get pointer to "data" in a module in Fortran 
            IntPtr pa = Fdll.get_a();

            // "Write" to unmanaged 'memory" using P/Invoke marshaling
            double[] val = { 123.4 };
            Marshal.Copy(val, 0, pa, val.Length);

            // Ouput the "data" using a method in Fortran
            Fdll.output_a();


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

      }
   }
}

Upon execution, you should get:

 --- Test Fortran Data Access ---
 out_a: a =    123.400000000000
Press any key to continue . . .

 

0 Kudos
Highlighted
Beginner
60 Views

Thank you for the reply, 

I am modernizing a 6,000 line structural analysis app for a customer: They want a more user friendly user interface on it. So, right now I'm just trying to come up with the most code-efficient, stable way to interoperate between the two languages. I know Fortran is very fast and easily parallelized for number crunching and the WPF user interfaces are much better than any of the Fortran user interfaces I've seen. 

So, after reading through your answers on both threads (Sorry about that, I thought the first one got lost in the ether and reposted). I think the best way forward for me is to write a get and set method for each variable in fortran and then make wrapper classes in C# that import those methods. That way I'm not mixing managed and unmanaged memory and just using the most standard ways of exporting and importing between the two languages. 

Below is a sample for anyone else in the same boat. (not saying this is what everyone should do, but it seems like the right direction for me).

The Fortran: 

module Foo
    implicit none      
    
    real*8 :: Bar
    
    contains
    
    SUBROUTINE GetBar(value)
        !DEC$ ATTRIBUTES DLLEXPORT :: GetBar
        !DEC$ ATTRIBUTES ALIAS: 'GetBar' :: GetBar
        !DEC$ ATTRIBUTES REFERENCE :: value
                    
        real*8, INTENT(OUT) :: value
        
        value = Bar
        
    END SUBROUTINE GetBar
    SUBROUTINE SetBar(value)
        !DEC$ ATTRIBUTES DLLEXPORT :: SetBar
        !DEC$ ATTRIBUTES ALIAS: 'SetBar' :: SetBar
        !DEC$ ATTRIBUTES REFERENCE :: value
                    
        real*8, INTENT(IN) :: value
        
        Bar = value
        
    END SUBROUTINE SetBar

END module

The C# Wrapper: 

public class Foo
{
    #region Imported Fortran Properties

    [DllImport("Fortran_Library.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern void GetBar(ref double val);
    [DllImport("Fortran_Library.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern void SetBar(ref double val);        
        
    #endregion

    #region C# Properties
    public static double Bar
    {
        get
        {
            double result = 0d;
            GetBar(ref result);
            return result;
        }
        set => SetBar(ref value);
    }
}

The C# Unit Tests: 
 

[TestClass()]
public class Foo_Tests
{        
    [TestMethod]
    public void BarTest()
    {
        Foo.Bar = 42.0;

        Assert.AreEqual(42.0, Foo.Bar);
    }     
}

Feel free to give me feedback on the code, I am a Novice at fortran compared to you guys. Let me know if any of you have a C# question, I've been doing that for a while :)



 

0 Kudos
Highlighted
Honored Contributor I
60 Views

Bowman, Eric wrote:

.. Feel free to give me feedback on the code, I am a Novice at fortran compared to you guys. Let me know if any of you have a C# question, I've been doing that for a while :)

What you show in Quote #4 appears an overkill.

You mention, "I am modernizing a 6,000 line structural analysis app for a customer" - does this mean the app is already in Fortran, perhaps in Windows DLLs (given your code samples)?

If yes, it presumably has "APIs" in the form of a few (or many) subroutines in Fortran to perform the structural app calculations?

But are DLL data, meaning 'global" entities in the Fortran layer all accessed directly using DLLEXPORT attributes at present?

Can you share a brief example (short pseudo code will suffice) for your Fortran layer?  That will help provide you with a few other options you can consider in terms of more user-friendly (and hopefully efficient) code structure (pun intended!) for your app that uses C# (WPF?) for your user interface but Fortran for the number-crunching!

0 Kudos
Highlighted
Beginner
60 Views

It is a standalone fortran console application, but there are quite a few subroutines. (The main program is only about 300 lines).

My plan is to refactor it a bit and put it into a dll so that I can call the calculations from Fortran as if it's an API. 

Thank you for your help :) 

(And I think the Unit tests are probably overkill, but they do double check that I haven't made any type-o's as I've written my wrappers). 

 

0 Kudos
Highlighted
New Contributor II
60 Views

Unless they are huge models, it would be faster to rewrite the Fortran code in C# and make it simple.

There good inversion routines in C# MATH.NET for example. 

John

0 Kudos
Highlighted
New Contributor I
60 Views

I have a very complex simulator with windows user interface written in C++ and the simulation modules (.exe and .dll) in Fortran.

No problems in calling Fortran DLL from C++ so I would expect no problems in calling the same dll from C#.

If you need to transfer data in both directions you may implement functions like GET and PUT in both C# executable and Fortran DLLs.

Regards 

0 Kudos
Highlighted
Honored Contributor I
60 Views

Bowman, Eric wrote:

It is a standalone fortran console application, but there are quite a few subroutines. (The main program is only about 300 lines).

My plan is to refactor it a bit and put it into a dll so that I can call the calculations from Fortran as if it's an API. 

Thank you for your help :) 

(And I think the Unit tests are probably overkill, but they do double check that I haven't made any type-o's as I've written my wrappers). 

 

With respect to Quote #4, what would be an overkill will be the setter+getter methods in both Fortran and C# for each module entity such as 'Bar', not the Unit Tests.  In terms of Unit Tests, if this application is of any value to your user base, then more the better and with unit testing framework in Visual Studio, you can harness an extremely large number of tests and manage them easily.

My other point was you can use the current Fortran main program, which as you state is only 300 lines, to design  a compact C# 'wrapper' class that conveniently and efficiently drives the computations with your user interface.  And over time, you can replace all the Fortran API's with suitable methods in C# as suggested upthread, especially if there is no other driver for Fortran code and these 'unmanaged' DLLs.

0 Kudos
Highlighted
Beginner
60 Views

Thanks for the help everyone! I've started implementing everything and it's going very smoothly. A Bit of repetitive coding, but I feel like that's the nature of wrappers. 

 

Eric

0 Kudos