Fortran Coder

标题: c++向Fortran编写的dll中传递字符串变成乱码 [打印本页]

作者: deserve0    时间: 2016-11-3 10:52
标题: c++向Fortran编写的dll中传递字符串变成乱码
本帖最后由 deserve0 于 2016-11-3 10:52 编辑

一、编程思路描述:
(1)用Fortran编写了一个DLL,此DLL中导出的子程序有一个参数,为字符串(字符串的长度不定,仅限英文)。
(2)用C++编写了主程序,调用此DLL,向DLL中传入一个字符串(例如路径名)。

二、出现的问题:
(1)传入DLL的字符串变成了乱码。如图:

三、代码:
(1)C++代码:
[Fortran] 纯文本查看 复制代码
#include <stdio.h>
#include <string.h>

extern "C" {void _stdcall   vlhm_forecast(char *);}
int main()
{
        char *inputfile="C:\\Users\\www\\Desktop\\model_2\\cpp_main\\31005700.tem";
        printf("before calling:\n");
        printf("filename=\"%s\"",inputfile);
        printf("\n");
        vlhm_forecast(inputfile);
}


(2)Fortran的DLL代码:
[Fortran] 纯文本查看 复制代码
subroutine vlhm_forecast(filename)

!DEC$ ATTRIBUTES STDCALL,DLLEXPORT::vlhm_forecast

character(len=*)::filename            !c++主程序中传递进来的“Inputfile”变量
write(*,*)"dll里的filename值是:"
write(*,*)filename

end subroutine



error1.png (22.58 KB, 下载次数: 313)

输出乱码图

输出乱码图

作者: fcode    时间: 2016-11-3 11:22
最重要的错误是,filename 必须是 REFERENCE 的(传址),因为 stdcall 默认是传值
但是由于 REFERENCE 的不能用假定长度(*),所以一般会要求传递一个表示字符串长度的量:

[Fortran] 纯文本查看 复制代码
subroutine vlhm_forecast(filename,lens)
!DEC$ ATTRIBUTES STDCALL,DLLEXPORT::vlhm_forecast
!DEC$ ATTRIBUTES REFERENCE :: filename
character(len=lens)::filename            !c++主程序中传递进来的“Inputfile”变量
integer lens
write(*,*)"dll in filename is:"
write(*,*)filename
end subroutine

[C++] 纯文本查看 复制代码
extern "C" {void _stdcall   vlhm_forecast(char *,int); }
int _tmain(int argc, _TCHAR* argv[])
{
  char *inputfile = "C:\\Users\\www\\Desktop\\model_2\\cpp_main\\31005700.tem";
  printf("before calling:\n");
  printf("filename=\"%s\"", inputfile);
  printf("\n");
  vlhm_forecast(inputfile,strlen(inputfile));
  return 0;
}



作者: fcode    时间: 2016-11-3 11:32
在这个过程中,你用到了很多不规范的用法。只能在 IVF 编译器上使用。

更规范的用法是,使用 ISO_C_Binding。它与上面的区别是,使用了 C 的调用协定(而不是 stdcall)

[Fortran] 纯文本查看 复制代码
subroutine vlhm_forecast( pfilename , lens ) Bind(C,Name="vlhm_forecast")
!DEC$ ATTRIBUTES DLLEXPORT :: vlhm_forecast
  use , Intrinsic :: ISO_C_Binding
  type(C_PTR) , value :: pfilename !c++主程序中传递进来的“Inputfile”变量,是C语言的指针
  integer , value :: lens  
  character(len=lens) , pointer :: filename     !这是fortran的字符串      
  call c_f_pointer( pfilename , filename ) !//把 c 语言的指针转换成fortran字符串
  write(*,*)"dll in filename is:"
  write(*,*)filename
end subroutine

[C++] 纯文本查看 复制代码
extern "C" {void vlhm_forecast(char *,int); }
int _tmain(int argc, _TCHAR* argv[])
{
  char *inputfile = "C:\\Users\\www\\Desktop\\model_2\\cpp_main\\31005700.tem";
  printf("before calling:\n");
  printf("filename=\"%s\"", inputfile);
  printf("\n");
  vlhm_forecast(inputfile,strlen(inputfile));
  return 0;
}

这样做的好处是,fortran代码可以不做任何修改的在 linux gcc (或其他平台)上编译。


作者: deserve0    时间: 2016-11-3 21:41
谢谢大神!!让我又有了更深的理解
作者: zhuhuanlai    时间: 2016-11-16 21:40
本帖最后由 zhuhuanlai 于 2016-11-16 22:28 编辑
fcode 发表于 2016-11-3 11:32
在这个过程中,你用到了很多不规范的用法。只能在 IVF 编译器上使用。

更规范的用法是,使用 ISO_C_Bindin ...

大神好,我在2013VS和2013IVF上再现上述程序时能输出FORTRAN的DLL文件,如下图一所示
我把DLL文件拷贝到C++(ConsoleApplication1)下的DEBUG文件内,如下图二所示

然后build  C++(ConsoleApplication1)工程时,出现以下错误如下图三所示:

补充说明,我的解决方案中包含了FORTRAN和C++两个工程,如下图四所示:


还请大神指点!


YWNXN)01VERI5N[189FE1UK.png (25.11 KB, 下载次数: 312)

图一

图一

I)1RUAEEH3GM(3GWEKW4KC1.png (30.05 KB, 下载次数: 281)

图二

图二

849(YD5GD_($LIX0ZMNGWY9.jpg (292.27 KB, 下载次数: 295)

图三

图三

0MX9ZBNQ$~IITFEW{MV[0Z0.jpg (280.94 KB, 下载次数: 292)

图四

图四

作者: fcode    时间: 2016-11-17 09:00
int main(){
就可以了。毕竟你只是测试。
作者: zhuhuanlai    时间: 2016-11-17 16:53
fcode 发表于 2016-11-17 09:00
int main(){
就可以了。毕竟你只是测试。

谢谢您的回复。
我把int _tmain(int argc, _TCHAR* argv[])   改成
int main(int argc, _TCHAR* argv[])         出现了同样的错误,是什么原因呢?
另外,这两种表达方式有什么区别呢?


作者: fcode    时间: 2016-11-17 18:51
我的意思是改成
int main()
去掉里面的参数

我对 VC++ 懂得不多,抱歉。
作者: zhuhuanlai    时间: 2016-11-17 21:04
fcode 发表于 2016-11-17 18:51
我的意思是改成
int main()
去掉里面的参数

感谢,我试试您的方法!




欢迎光临 Fortran Coder (http://bbs.fcode.cn/) Powered by Discuz! X3.2