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

Calling a C .DLL from Fortran

YertleTheTurtle
Beginner
1,519 Views
Hello:

I've got this .DLL written by someone else that I need to call from my Fortran program. It is either C or C++, not sure.

None of the examples in the compiler documentation cover this combination.

My old compiler used the following statement (along with a compiler directive
to use Microsoft Mixed language name decoration) to pass the variable "TString"
to/from the .DLL and it worked fine:

dll_import TString

So, with the Intel compiler, I found the following suggestion in the knowledge database:

!DEC$ ATTRIBUTES DLLIMPORT, ALIAS: 'TString' ::TString

if I try that, I get an unsatisfied external error message, saying the the linker cannotresolve __imp_Tstring

If I try

!DEC$ ATTRIBUTES DLLIMPORT, ALIAS: 'TString' ::__imp_TString

the unsatisfied external error reference changes to _TSTRING

Trying

!DEC$ ATTRIBUTES DLLIMPORT, ALIAS: 'TString' ::__imp_TSTRING

makes no difference.

I have put copies of the .DLL in the directory where the object and .EXE files
reside and in the directory where the source files reside without success.

So, now I`ve run out of ideas.

Does anyone have any

Thanks
0 Kudos
10 Replies
DavidWhite
Valued Contributor II
1,519 Views
you may need to add the STDCALL attribute to your declaration to allow for the differences between C and Fortran.

!DEC$ ATTRIBUTES DLLIMPORT, STDCALL, ALIAS: 'TString' ::TString

David
0 Kudos
anthonyrichards
New Contributor III
1,519 Views
Use DUMPBIN on your C DLL to see what the actual symbol used for your routine/function Tstring. It may or may not have a leading underscore or trailing '@n' where 'n' is an integer equal to 4 times the number of arguments. It may be mixed case ('TString') or uppercase ('TSTRING'). In the worst case, if the C DLL was not compiled with declarations using 'extern "C" then the DLL symbols will be a 'mangled' as well. You have to get the alias exactly right to match what is in the DLL's symbol table.
0 Kudos
Steven_L_Intel1
Employee
1,519 Views
A couple of problems here.

First, do not use "__imp_" in your code. This string is added by the linker for DLLIMPORTed symbols. So your first line:

!DEC$ ATTRIBUTES DLLIMPORT, ALIAS: 'TString' ::TString

is almost correct. I would change this to:

!DEC$ ATTRIBUTES DLLIMPORT, DECORATE, ALIAS: 'TString' ::TString

Omitting DECORATE will prevent the leading underscore from being added on IA-32.

Second, the DLL is not involved in the build process. You want instead to add the .LIB, that was created when the DLL was built, to your project or to Additional Dependencies under Linker properties.

You can use "dumpbin -exports" on the .LIB to see what symbols it exports.
0 Kudos
YertleTheTurtle
Beginner
1,519 Views
Thanks, but none of these suggestions work.

First of all, it's been so long since I recompiled my program that I forgot
that I needed the .LIB files.

Once I looked at those I found that the variable being exported is named:

_TString@12

In my program, it is an integer function with three arguments, so the '@12' suffix makes sense.

The old calling sequence is

integer=TSTRING(A,B, Val(n))

where A and B are strings and Val is a non-standard feature of the old
compiler (call by value). I presume the Intel compiler has an equivalent somewhere.

I removed the "Val" for the moment and changed

integer(kind=4) :: TString

to

integer(kind=4), external :: TString

(otherwise DECORATE won't work)

and tried every combination of the ATTRIBUTES properties that I could think of.

One possibility is

!DEC$ ATTRIBUTES DLLIMPORT, STDCALL, DECORATE :: TString

which gives an error saying that __imp__tstring@12 is an unsatisfied external.

Leaving out the STDCALL changes the case and removes the suffix so that
the unsatisfied external becomes __imp__TSTRING

If I use ALIAS, it seems to negate the DECORATE keyword.

!DEC$ ATTRIBUTES DLLIMPORT, DECORATE, ALIAS : 'TString' :: TString

and the unsatisfied external becomes __imp__TString

Adding STDCALL to this does nothing.

The closest I could come was the following:

!DEC$ ATTRIBUTES DLLIMPORT, STDCALL, DECORATE , ALIAS : 'TString@12' :: TString

which gives the error

unresolved external symbol __imp__TString@12 referenced


At least the case is now correct, as is the suffix. Now, how do I get rid of
the "__imp__" or the excess underscores if the linker is really adding "_imp_" ?

Thanks for any suggestions.
0 Kudos
YertleTheTurtle
Beginner
1,519 Views
With reference to my previous posting and if anyone else needs to know, the final try:

!DEC$ ATTRIBUTES DLLIMPORT, STDCALL, DECORATE , ALIAS : 'TString@12' :: TString

seems to work - at least it linked OK !!

I had the linker LIBPATH set incorrectly.

Now, does anyone know a quick answer to the following:

How do you pass 3 variables to a C routine when the first two are by reference, and the last one is by value?

The ATTRIBUTES syntax doesn't seem to allow mixed references.

Thanks

0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,519 Views
Oh, you had confused as all:

"Once I looked at those I found that the variable being exported is named:

_TString@12

In my program, it is an integer function with three arguments
"

Variable is not a Function. Those are two distinct categories: you use variables, but you call functions (and subroutines, generally referred to as procedures). From the start, we thought that TString is a variable, but it was a procedure all the way.

Now, with that cleared up: your solution works, but it is not exactly nice.

All the time, the problem was that the compiler could not figure out the "@12" part (meaning, there are 12/4 = 3 arguments to the function). When it "sees" the call integer=TSTRING(A,B, Val(n)), it's too late, because you had to tell it ALIAS beforehand.

The straightforward solution is to tell the compiler in advance about the prototype of the function: you should put an INTERFACE block somewhere in the declaration part, like:

[fortran]INTERFACE
   INTEGER FUNCTION TString(A, B, N)
   !DEC$ATTRIBUTES STDCALL, REFERENCE, DECORATE, ALIAS: "TString":: TString
   !Correct the following, I made a guess:
   REAL:: A
   REAL:: B
   !DEC$ATTRIBUTES VALUE:: N
   INTEGER:: N
   END FUNCTION
END INTERFACE[/fortran]
That should also answer your last question: REFERENCE attribute specifies that reference is the default, but VALUE for N overrides it. You call it just like

[bash]i = TString(x, y, i)[/bash]

0 Kudos
Steven_L_Intel1
Employee
1,519 Views
With reference to my previous posting and if anyone else needs to know, the final try:

!DEC$ ATTRIBUTES DLLIMPORT, STDCALL, DECORATE , ALIAS : 'TString@12' :: TString

seems to work - at least it linked OK !!


That would be incorrect - the alias string should be 'TString'. DECORATE will add the @12 and the leading underscore.

You can specify VALUE or REFERENCE as an attribute on individual arguments.

0 Kudos
YertleTheTurtle
Beginner
1,519 Views
Hi:

I tried those solutions, and they worked in combination.

Thank you all.

A couple of questions, just to clarify my understanding:

1. Why must the statement

!DEC$ ATTRIBUTES DLLIMPORT, STDCALL, reference, DECORATE, ALIAS: "TString":: TString

ONLY appear once, and ONLY in the interface block? If it appears twice, once in the INTERFACE and once in the actual subroutine that calls TString, you get an inscrutable Fortran error message as follows:

"The attributes of this name conflict with those made accessible by a USE statement"

even if both ATTRIBUTES statements are identical.

2. What does !DEC$ stand for? Is this a reference to some ancient DEC compiler extension?

Again, thanks.

Yert






0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,519 Views
1) You did not show us the code, but as I get it, you have the INTERFACE in a MODULE, and then you again declare the attributes in the subroutine that USEs that module? The Fortran philosophy is, simply, that you may not redeclare things (although there are exceptions that can get really complicated), even if they are identical. I find it simple and straightforward enough.

2) Yes, !DEC$ originally stands for that DEC, because it is a significant part of Intel Fortran heritage, and the extension indeed draws from these days. When Intel acquired Compaq Fortran (which acquired Digital/DEC around 1998), they invented a backronym "Directive Enhanced Compilation" for it, but it did not really caught on (not that they pushed it very hard).
0 Kudos
Steven_L_Intel1
Employee
1,519 Views
We invented the "backronym" just to placate a few in management who questioned it. It worked. We all know what it really means...

But if it bothers you, you can use !DIR$ instead.
0 Kudos
Reply