Calling a function defined in a C# exe from a Fortran dll

Calling a function defined in a C# exe from a Fortran dll

Hi, 

Appologies if this is a duplicate - I've done a few searches and cannot quite find what I'm looking for... I'm hoping someone here can help me to do this in a "best practise" way using the Fortran/C interop. 

Basically I have a "number cruching" dll written in using Intel Fortran which runs under a C# front end. As this could take some time I would like the dll to be able to call back the exe to inform it of its current progress. 

The basic calling procedure I envisage is:

  1. C# passes Fortran the memory address of the C# routine to call in the exe when progress has changed (as a delegate?)
  2. Fortran dll updates a module procedure pointer to point to this C# routine so that it can call it later on
  3. C# starts the main fortran "DoWork" function
  4. At intermediate points in "DoWork" the C# routine is called via the pointer and C# can update the display

If anyone could point me in the right direction, or even post a small complete code - (a simple console app which passes something like an int back) I would be very greatful. 

Cheers,

Michael

6 帖子 / 0 全新
最新文章
如需更全面地了解编译器优化,请参阅优化注意事项

Michael,

See if the attached is of any help,

 

 

附件: 

附件尺寸
下载 fortran-calls-c.docx3.1 KB

Thank you - this was a huge help. I've now successfully got this working.

The only amendment to your suggested route was the Fortran declaration when passing the delegate. When this was given the pointer attribute I was getting memory corruption, however by defining it simply as "PROCEDURE(xxx)" (no pointer or intent attributes) the problem seems to have been solved. The module pointer can then be assigned using "=>". 

I've shown my C# and Fortran below, code just in case anyone else finds it helpful.

Michael

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
 class Program
 {
 [DllImport(@"CallbackTest.dll", CallingConvention = CallingConvention.Cdecl)]
 public static extern void SetupFunction([MarshalAs(UnmanagedType.FunctionPtr)] MyDelegate del);
 [DllImport(@"CallbackTest.dll", CallingConvention = CallingConvention.Cdecl)]
 public static extern int RunFortranCallbackFunction();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
 public delegate void MyDelegate(ref int val);
[STAThread]
 static void Main()
 {
 // Set the delegate then pass to fortran
 MyDelegate del = new MyDelegate(CSharpWriteToConsole);
 SetupFunction(del);
// Call a fortran routine which will callback 'CSharpWriteToConsole '
 RunFortranCallbackFunction();
 }
internal static void CSharpWriteToConsole(ref int i)
 {
 Console.WriteLine(i);
 }
 }
}

MODULE CallbackDll 
 IMPLICIT NONE
 PROCEDURE(CSharpWriteToConsoleInterface), POINTER :: CSharpWriteToConsole
 ABSTRACT INTERFACE
 SUBROUTINE CSharpWriteToConsoleInterface(i)
 INTEGER, INTENT(INOUT) :: i
 END SUBROUTINE CSharpWriteToConsoleInterface
 END INTERFACE
 CONTAINS
!DEC$ ATTRIBUTES DLLEXPORT::SetupFunction
!DEC$ ATTRIBUTES ALIAS:'SetupFunction'::SetupFunction
SUBROUTINE SetupFunction(fPtr) 
 PROCEDURE(CSharpWriteToConsoleInterface) :: fPtr
 INTEGER :: i
 ! Set Pointer
 CSharpWriteToConsole => fPtr
END SUBROUTINE SetupFunction
!DEC$ ATTRIBUTES DLLEXPORT::RunFortranCallbackFunction
!DEC$ ATTRIBUTES ALIAS:'RunFortranCallbackFunction'::RunFortranCallbackFunction
FUNCTION RunFortranCallbackFunction() result(i)
 INTEGER :: i, tmp
DO i = 1, 10 
 tmp = i 
 CALL CSharpWriteToConsole(tmp)
 END DO
END FUNCTION RunFortranCallbackFunction
END MODULE CallbackDll

Glad to see it worked out.  Great catch on the PROCEDURE declaration in the SetupFunction - sorry for the mislead in my post, I was doing it from memory.

I cannot pretend to understand all the code, but it's pretty coolstuff.

Posters needing all types of C# - Fortran interplay will be beating a path to your door as a result IMO!

Thanks for the code. I use it now in my actual project!

Markus

发表评论

登录添加评论。还不是成员?立即加入