Fortran Coder

查看: 13826|回复: 6
打印 上一主题 下一主题

[混编] C#与fortran混合编程的问题

[复制链接]

58

帖子

9

主题

0

精华

熟手

F 币
256 元
贡献
163 点
跳转到指定楼层
楼主
发表于 2016-11-21 23:30:21 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
大侠好,我用C#调用fortran的DLL文件,对两个项目分别编译,均显示成功。运行C#时出现以下错误,还请抽空帮忙指点,不胜感激!
托管调试助手“PInvokeStackImbalance”在“C:\Users\ZHL\Desktop\ConsoleApplication1\ConsoleApplication1\bin\Debug\ConsoleApplication1.vshost.exe”中检测到问题。
其他信息: 对 PInvoke 函数“ConsoleApplication1!ConsoleApplication1.Program::Add”的调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配。
C#代码如下:
[C#] 纯文本查看 复制代码
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace ConsoleApplication1
{
    class Program
    {
        [DllImport(@"C:\Users\ZHL\Desktop\ConsoleApplication1\Dll1\Debug\Dll1.dll", EntryPoint = "Add")]     //指定DLL路径,声明外部Add
        extern static int Add(int A,int B);
        static void Main(string[] args)
        {
            int c = Add(1,2);
            Console.WriteLine(c);    //打印c值,正确值为3
            Console.Read();
        }
    }
}

FORTRAN中代码如下:
[Fortran] 纯文本查看 复制代码
subroutine Add(A,B)  Bind(C,Name="Add")
!DEC$ ATTRIBUTES DLLEXPORT :: Add
use , Intrinsic :: ISO_C_Binding
    implicit none
    integer A,B 
    return A+B
    RETURN
  end subroutine

补充说明:我担心是不是fortran区分大小写导致错误,把C#和fortran程序中Add都改为ADD也出现同样的问题,如果需要,我可以把整个文件传上来。
分享到:  微信微信
收藏收藏1 点赞点赞 点踩点踩

2033

帖子

12

主题

5

精华

论坛跑堂

臭石头雪球

F 币
1641 元
贡献
709 点

美女勋章热心勋章星光勋章新人勋章贡献勋章管理勋章帅哥勋章爱心勋章规矩勋章元老勋章水王勋章

沙发
发表于 2016-11-22 09:24:07 | 只看该作者
Fortran不区分大小写,反之 c/c++/c# 区分大小写。
因此,Fortran里的 Bind(c,Name="Add") 此处的 Add 区分大小写。其他的都不区分。比如参数是 A 和 B,但是我写成 a+b 也可以。
[Fortran] 纯文本查看 复制代码
Integer Function Add(A,B)  Bind(C,Name="Add")!//要返回的话,请用 Function
!DEC$ ATTRIBUTES DLLEXPORT :: Add
  use , Intrinsic :: ISO_C_Binding
  implicit none
  integer , value :: A , B !/c#默认是传值,而fortran默认传址。因此要用value修饰
  Add = a + b !// Add(函数名)用于返回,而不能写 return a+b
end Function Add





58

帖子

9

主题

0

精华

熟手

F 币
256 元
贡献
163 点
板凳
 楼主| 发表于 2016-11-22 21:25:31 | 只看该作者
本帖最后由 zhuhuanlai 于 2016-11-22 21:27 编辑
zhuhuanlai 发表于 2016-11-22 21:21
感谢大侠的及时指点,我成功的再现了您的方法,再次感谢!
由于个人水平非常有限,有些尚不明白,还请耐 ...

感谢大侠的及时指点,我成功的再现了您的方法,再次感谢!
由于个人水平非常有限,有些尚不明白,还请耐心指点!
1、        对函数/子程序,我的理解是C#程序须与Bind(C,Name="函数/子程序名称")中的大小写形式保持一致,而FORTRAN程序内其他地方的大小写无所谓。
2、        对变量名称,我的理解是C#程序须与FORTRAN中定义变量语句如integer , value :: A , B中的大小写形式一致,而FORTRAN程序内其他地方的大小写无所谓。
3、        对subroutine和function的,我的理解是子程序和函数类似,唯一不同的是函数可以有返回值,而子程序没有。我见过一些FORTRAN的DLL文件,大都采用subroutine,对这两种的选择有点困惑。我用C#主要做界面,运行时需要从C#中输入几十个数据(可能有数组),需要传入到FORTRAN程序中,在FORTRAN中计算后再把计算结果(包含数据和字符)返回到C#界面,这样的情况应该采用哪种呢?
4、        ISO_C_Binding不会自动更改参数传递约定,还需用value修饰来更改成传值方式?那它的作用除了清理栈道和命名约定外,在其他方面就不再有作用了(比如参数,数组和字符串的传递约定)?
5、        ISO_C_Binding中变量对应关系如图一:


一个fortran整数类型对应的类型如此多,而C#与C的类型还不完全一样如图二所示:

最容易弄混的是就是long,char两个类型,在C/C++中long和int都是4个字节,都对应着C#中的int类型,而C/C++中的char类型占一个字节,用来表示一个ASCII码字符,在C#中能够表示一个字节的是byte类型。与C#中char类型对应的应该是C/C++中的wchar_t类型,对应的是一个2字节的Unicode字符。
如果ISO_C_Binding不会自动更改参数传递约定,那我用C#调用FORTRAN的DLL,是否没有太大的帮助呢?


MC%}{]@X1ILX04A}TNAG{}D.png (31.52 KB, 下载次数: 359)

图一

图一

TBF}HZ9`356~4{FTN$}PHL3.png (16.42 KB, 下载次数: 340)

图二

图二

2033

帖子

12

主题

5

精华

论坛跑堂

臭石头雪球

F 币
1641 元
贡献
709 点

美女勋章热心勋章星光勋章新人勋章贡献勋章管理勋章帅哥勋章爱心勋章规矩勋章元老勋章水王勋章

地板
发表于 2016-11-22 21:48:04 | 只看该作者
1. 不同的语言(甚至同一个语言的不同编译器),都会对函数名字进行修饰(符号修饰),比如把 Add 变成 _Add@8
  对于面向对象语言,由于存在“函数重载”“多态”,可能同一个函数有不同的版本(函数签名不同),或者不同的“namespace”有同名的函数
  因此,它们的符号修饰更夸张,比如把 func 修饰为 _ZN1C2C24funcEi
  在涉及混编的时候,需要把“符号修饰”统一起来(否则一个语言找不到另一个语言的某个函数的真实名字)
  Bind( C ,Name="某某" 就是实现把 fortran 的函数名,用 C 标准的符号修饰。因此,它必须与C#里 dllimport 的名字相同。
2. 一旦找到了函数名,那么参数的名字不需要保持一致。即 C# 里传递的实参是 x y ,而fortran的虚参可以是 a b(或其他名字)
3. 在 Fortran 里 function 与 C# 的 func(){} 类似。subroutine 与 void func() {} 类似。区别仅仅在于此。
   用 function 也可以,用 subroutine 也可以。(虽然 subroutine 没有返回值,但可以通过指针传递参数,或引用传递参数来交换数据)
4. ISO_C_Binding 只规定需要统一的内容,不需要规定的内容由程序员自己控制。
   比如传址还是传值?fortran默认传址,但是可以用 value 修饰变成传值。
   c/c++/c# 默认传值,但可以通过 引用调用(或指针)变成传值。
   因此,ISO_C_binding并不规定到底如何传递,由程序员按需求使用。(但是调用者和被调用者要保持一致)例如此例子,你可以不用 value 修饰,而在 C# 里面把
extern static int Add( int A , int B);
更改为
extern static int Add( ref int A , ref int B);
也是可以的。
5. 就数据类型而言,fortran是比较少的,远远不如 c/c++/c# 多。
   但是,在底层,数据类型其实只有一种(二进制),上层规定出来这么多种。是因为解释和操作的方式不一样。
   c#把自己的任何数据类型传递过来,在底层都是传递了若干字节的二进制。然后 fortran 再用它自己识别的数据类型来解释和操作这些二进制数据。
   
   规定 C_INT , C_SHORT , C_LONG_LONG 这些类型,只是为了消除不同平台(不同编译器)的差异。
   因为某些编译器的 short 占2字节,long 占4字节。但是某些平台的 short占4字节,long占8字节
   
   不管你用何种平台,何种编译器。ISO_C_binding 可以保证在相同平台下的 C/C++/C# 编译器与对应的 ISO_C_binding 中规定的相应类型一致。
   而不需要专门为某种平台修改代码。
   
   它并不能解决哪些“fortran”语言不识别的数据类型(例如unsigned,wchar,tchar)
   
ISO_C_Binding 其实是与 C 语言的交互,而非 C#。所以,在需要两者相互传递的时候,应该把各自上层(面向对象,类)数据转换到较底层的数据进行交互。

58

帖子

9

主题

0

精华

熟手

F 币
256 元
贡献
163 点
5#
 楼主| 发表于 2016-11-23 21:11:35 | 只看该作者
fcode 发表于 2016-11-22 21:48
1. 不同的语言(甚至同一个语言的不同编译器),都会对函数名字进行修饰(符号修饰),比如把 Add 变成 _Ad ...

拜谢大侠的指点!
就像黑夜海上航行的导航灯,给我点亮了前进的方向,真心感谢!

5

帖子

2

主题

0

精华

入门

F 币
32 元
贡献
16 点
6#
发表于 2020-12-30 11:05:10 | 只看该作者
请问我用同样的方法没有成功,引发的异常:“System.BadImageFormatException”(位于 MixedProgram3.exe 中)
“System.BadImageFormatException”类型的未经处理的异常在 MixedProgram3.exe 中发生
试图加载格式不正确的程序。 (异常来自 HRESULT:0x8007000B)

1

帖子

0

主题

0

精华

新人

F 币
11 元
贡献
3 点
7#
发表于 2021-4-12 11:35:37 | 只看该作者
关注这个问题。
您需要登录后才可以回帖 登录 | 极速注册

本版积分规则

捐赠本站|Archiver|关于我们 About Us|小黑屋|Fcode ( 京ICP备18005632-2号 )

GMT+8, 2024-12-24 21:41

Powered by Tencent X3.4

© 2013-2024 Tencent

快速回复 返回顶部 返回列表