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

Calling Fortran from C

Amir_Filipovic
Beginner
2,433 Views
Hi,

I have a C-code with calls to Fortran subroutines which doesn't work. I wonder if anybody can guess what I'm doing wrong?

The fortran code isdefined as below:

SUBROUTINE FLYCUR2(datafile,grffile)

!DEC$ ATTRIBUTES DLLEXPORT, STDCALL, REFERENCE :: FLYCUR2

!DEC$ ATTRIBUTES ALIAS: 'FLYCUR2' :: FLYCUR2

CHARACTER*(*) datafile, grffile

...

It's compiled with Intel Fortran compiler v.10 into a dll. I have tested it with a test example written in C# and it works.

The C code is defined as below:

extern int __stdcall FLYCUR2(char *,int,char *,int);

...
FLYCUR2(filename1,n1,filename2,n2);

...

Visual Studio 2008 is used to create a project with theC code. The fortran library (.lib) from the fortrancode is included in "Aditional Depencencies" with the linker.

However when I tried to build the code I get the following error:

Error50error LNK2019: unresolved external symbol __imp__FLYCUR2 referenced in function _print_pages@0GRAFDLL.objGraf_09

Any idea what I'm doing wrong?

0 Kudos
23 Replies
DavidWhite
Valued Contributor II
2,025 Views
I think your declarations need to be

!DEC$ ATTRIBUTES DLLEXPORT, STDCALL, ALIAS: 'FLYCUR2' :: FLYCUR2
!DEC$ ATTRIBUTES REFERENCE :: DATAFILE, GRFFILE

0 Kudos
Jugoslav_Dujic
Valued Contributor II
2,025 Views
Quoting - Amir Filipovic
Hi,

I have a C-code with calls to Fortran subroutines which doesn't work. I wonder if anybody can guess what I'm doing wrong?

The fortran code isdefined as below:

SUBROUTINE FLYCUR2(datafile,grffile)

!DEC$ ATTRIBUTES DLLEXPORT, STDCALL, REFERENCE :: FLYCUR2

!DEC$ ATTRIBUTES ALIAS: 'FLYCUR2' :: FLYCUR2

CHARACTER*(*) datafile, grffile

...

It's compiled with Intel Fortran compiler v.10 into a dll. I have tested it with a test example written in C# and it works.

extern int __stdcall FLYCUR2(char *,int,char *,int);

...
Any idea what I'm doing wrong?


Two things:
  1. STDCALL attribute causes lowercase name decoration, i.e. "flycur2". You can switch it to uppercase by adding ALIAS attribute, but then also use DECORATE, i.e. !DEC$ATTRIBUTES DLLEXPORT, STDCALL, REFERENCE, DECORATE, ALIAS: "FLYCUR2"
  2. In Intel fortran, the default passing of string length arguments is at the end of argument-list, i.e. (char*, char*, int, int). (You can get the old, Compaq Visual Fortran behavior, by specifying /iface:cvf switch or /mixed_str_len_arg, but I advise against it). As David said, you can suppress the length passing entirely by specifying !DEC$ATTRIBUTES REFERENCE:: datafile, grffile (but then you must hard-code the length on Fortran side).


0 Kudos
Amir_Filipovic
Beginner
2,025 Views
Quoting - David White
I think your declarations need to be

!DEC$ ATTRIBUTES DLLEXPORT, STDCALL, ALIAS: 'FLYCUR2' :: FLYCUR2
!DEC$ ATTRIBUTES REFERENCE :: DATAFILE, GRFFILE


I don't think the problem is in my declaration beacause, as I wrote before, I run the code from C# without any problem. The C# declaration is

[DllImport("sacudll.dll", CallingConvention = CallingConvention.StdCall)]
public static extern long FLYCUR2(String inFile, String outFile);

However I tried the declaration as you proposed and got an error.

0 Kudos
anthonyrichards
New Contributor III
2,025 Views
Is it because you have defined FLYCUR2 as an integer function, but you reference it as a VOID function? e.g.

extern int __stdcall FLYCUR2(char *,int,char *,int);

...
FLYCUR2(filename1,n1,filename2,n2);


Maybe you should write

i=FLYCUR2(filename1,n1,filename2,n2) ?

In which case it should be defined as an INTEGERFUNCTION on the Fortran side.

On the other hand, if you are defining it as SUBROUTINE on the Fortran side, then you should change the prototype to a VOID function on the C side.
0 Kudos
Amir_Filipovic
Beginner
2,025 Views
Quoting - Jugoslav Dujic

Two things:
  1. STDCALL attribute causes lowercase name decoration, i.e. "flycur2". You can switch it to uppercase by adding ALIAS attribute, but then also use DECORATE, i.e. !DEC$ATTRIBUTES DLLEXPORT, STDCALL, REFERENCE, DECORATE, ALIAS: "FLYCUR2"
  2. In Intel fortran, the default passing of string length arguments is at the end of argument-list, i.e. (char*, char*, int, int). (You can get the old, Compaq Visual Fortran behavior, by specifying /iface:cvf switch or /mixed_str_len_arg, but I advise against it). As David said, you can suppress the length passing entirely by specifying !DEC$ATTRIBUTES REFERENCE:: datafile, grffile (but then you must hard-code the length on Fortran side).



1. I tried to call to "flycur2" in the C-code without success.

2. I also tried to arange call parameters as you proposed without success. Further I tested toomit intparameters in the call- no success.
0 Kudos
Amir_Filipovic
Beginner
2,025 Views
Quoting - anthonyrichards
Is it because you have defined FLYCUR2 as an integer function, but you reference it as a VOID function? e.g.

extern int __stdcall FLYCUR2(char *,int,char *,int);

...
FLYCUR2(filename1,n1,filename2,n2);


Maybe you should write

i=FLYCUR2(filename1,n1,filename2,n2) ?

In which case it should be defined as an INTEGERFUNCTION on the Fortran side.

On the other hand, if you are defining it as SUBROUTINE on the Fortran side, then you should change the prototype to a VOID function on the C side.

I tried with

externvoid __stdcall FLYCUR2(char *,int,char *,int);

but I got the same error.
0 Kudos
TimP
Honored Contributor III
2,025 Views
Quoting - Amir Filipovic

I tried with

externvoid __stdcall FLYCUR2(char *,int,char *,int);

but I got the same error.
Looks like it might be worth while to upgrade the compiler and USE iso_c_binding. The legacy syntax can be made to work, but it's certainly not worth wasting time on it.
0 Kudos
Jugoslav_Dujic
Valued Contributor II
2,025 Views
Quoting - Amir Filipovic

I tried with

externvoid __stdcall FLYCUR2(char *,int,char *,int);

but I got the same error.

You seem to have missed my post above. Please read it. (Yes, you do need void as well, but that's a secondary issue.)

edit: ...Oh, and extern "C", if it's a C++ compiler:

extern "C" void __stcall flycur2(char*, char*, int, int);
0 Kudos
Steven_L_Intel1
Employee
2,025 Views
You can't use BIND(C) with STDCALL in our implementation.

The original problem, with the linker error, indicates that the C/C++ code has specified "dllimport" but the library being linked against is not the export library from a corresponding build of the DLL. Note the __imp_ prefix to the name.

Now I notice something interesting. The name generated by building the DLL should be __imp_FLYCUR2 with one underscore before the FLYCUR2, but the linker error message has two underscores. This makes me think that the name as defined in the C/C++ code is actually _FLYCUR2.
0 Kudos
Amir_Filipovic
Beginner
2,025 Views
Quoting - Jugoslav Dujic

You seem to have missed my post above. Please read it. (Yes, you do need void as well, but that's a secondary issue.)

edit: ...Oh, and extern "C", if it's a C++ compiler:

extern "C" void __stcall flycur2(char*, char*, int, int);

Yes I've read your post and I've tested

extern void __stdcall flycur2(char *,char *,int,int);

flycur2(filename1,filename2,n1,n2);

and I've got the following error

Error1error LNK2019: unresolved external symbol _flycur2@16 referenced in function _print_pages@0GRAFDLL.objGraf_09

I've also tested

extern "C" void __stdcall flycur2(char *,char *,int,int);

flycur2(filename1,filename2,n1,n2);


and I've got a syntax error.

I've tested so many different options now that I'm beginning to suspect if the linker really seesthe library? The .lib file is included in "Additional Dependencies" and the .dll file is in the working directory. That should be enough - or maybe not??

0 Kudos
Steven_L_Intel1
Employee
2,025 Views
From a Fortran Build Environment command prompt, set default to the folder with the .lib and do this:

dumpbin -exports yourlibrary.lib

where yourlibrary.lib is the name of the export library. What does it show? Note that the names displayed will lack the __imp prefix.
0 Kudos
Amir_Filipovic
Beginner
2,025 Views
From a Fortran Build Environment command prompt, set default to the folder with the .lib and do this:

dumpbin -exports yourlibrary.lib

where yourlibrary.lib is the name of the export library. What does it show? Note that the names displayed will lack the __imp prefix.

Here is the result:

Dump of file sacudll.lib

File Type: LIBRARY

Exports

ordinal name

FLYCUR2

Summary

C9 .debug$S
14 .idata$2
14 .idata$3
4 .idata$4
4 .idata$5
E .idata$6


0 Kudos
Steven_L_Intel1
Employee
2,025 Views
Ok - that tells you that the name is upcased from Fortran. Now open that .lib in Notepad and find all instances of FLYCUR. What is the exact symbol shown?
0 Kudos
Amir_Filipovic
Beginner
2,025 Views
Ok - that tells you that the name is upcased from Fortran. Now open that .lib in Notepad and find all instances of FLYCUR. What is the exact symbol shown?

J__IMPORT_DESCRIPTOR_SacuPC_09 __NULL_IMPORT_DESCRIPTOR
SacuPC_09_NULL_THUNK_DATA FLYCUR2 __imp_FLYCUR2
FLYCUR2 __IMPORT_DESCRIPTOR_SacuPC_09 __NULL_IMPORT_DESCRIPTOR __imp_FLYCUR2
SacuPC_09_NULL_THUNK_DATA SacuPC_09.dll
FLYCUR2 SacuPC_09.dll
0 Kudos
Steven_L_Intel1
Employee
2,025 Views
Great - that looks fine. Note that this is not a STDCALL-decorated name. Now do this for your C++ object:

dumpbin -symbols thisobj.obj > dump.txt

and then open dump.txt in notepad and find all occurences of FLYCUR
0 Kudos
Amir_Filipovic
Beginner
2,025 Views
Great - that looks fine. Note that this is not a STDCALL-decorated name. Now do this for your C++ object:

dumpbin -symbols thisobj.obj > dump.txt

and then open dump.txt in notepad and find all occurences of FLYCUR

0DA 00000000 UNDEF notype () External | _flycur2@16
0 Kudos
Lorri_M_Intel
Employee
2,025 Views

The Fortran name has only one underscore, and the C code is looking for two.

The Win32 calling convention is that a leading underscore is added to the external name - in this case before the __imp_ is prepended - therefore giving the external name __imp__FLYCUR2 being asked for by the C code.

When you use the ALIAS: "FLYCUR2" the compiler assumes you know EXACTLY what name you want, and so does not put on the leading underscore.

Try adding DECORATE to your DEC$ ATTRIBUTES line. That keyword tells the compiler to treat the "alias" name as a base, and to add any appropriate "decorations" required by the calling convention.

- Lorri
0 Kudos
Steven_L_Intel1
Employee
2,025 Views
Ah, Lorri found the other piece of the puzzle. Actually, looking at what you have, take out the ALIAS entirely since your C++ code is now looking for the symbol that Fortran would create by default when STDCALL is specified.
0 Kudos
Amir_Filipovic
Beginner
2,025 Views
Ah, Lorri found the other piece of the puzzle. Actually, looking at what you have, take out the ALIAS entirely since your C++ code is now looking for the symbol that Fortran would create by default when STDCALL is specified.

Yes, finally!!!! Removing

!DEC$ ATTRIBUTES ALIAS: 'FLYCUR2' :: FLYCUR2

solved the problem. What a work for such a simple change! But I still wonder why it works when I call it from C#?

Thank you guys for your help!!!

0 Kudos
Steven_L_Intel1
Employee
1,892 Views
It won't work from C# anymore as now the name is wrong. If you want to use it from both C++ and C# you're going to have to use a declaration on the C++ side to set the name to something that C# can use and then go back to using ALIAS.
0 Kudos
Reply