The following interface works when the function I am calling is in a C DLL built with a cdecl calling convention. What do I change if the DLL is replaced with one having a stdcall convention?
function TA_GetFeatureValue(handle, featureName, lpValueStr, cchValue) bind(c, name = 'TA_GetFeatureValue') result(retval) import integer(c_int32_t), value :: handle ! Note that Fortran doesn't have unsigned integer types integer(c_short), dimension(*) :: featureName integer(c_short), dimension(*) :: lpValueStr integer(c_int), value :: cchValue ! Note the value attribute here integer(c_long) :: retval end function
If you're on Windows OS and the DLL and the application are built for 64-bit platform (e.g., x64 configuration in Visual Studio), then 'stdcall' should make no difference and you should not require any changes.
However, if the target platform is 32-bit (e.g., x86 configuration in Visual Studio for the C DLL, etc.), then you can try including the !DIR$ ATTRIBUTES for STDCALL - see this link.
function TA_GetFeatureValue(handle, featureName, lpValueStr, cchValue) bind(c, name = 'TA_GetFeatureValue') result(retval) !dir$ attributes stdcall :: TA_GetFeatureValue ..
I'm close, but not quite there. I replaced the cdecl DLL with the stdcall, and tried running the program (a console program), and it worked! But at END PROGRAM throws an exception about the stack. No surprise there.
I added the ATTRIBUTES STDCALL statements and the program would not link, with errors like the following.
unresolved external symbol _TA_GetFeatureValue@16 referenced in function _MAIN__
I tried adding an underscore in front of the routine name like this:
!dir$ attributes stdcall :: _TA_GetFeatureValue
The program would build and run, but at exit throws the same exception about the stack.
I tried adding an alias statement such as the following, but the compiler said The BIND(C) attribute for this symbol conflicts with a DEC$ ATTRIBUTES ALIAS
!DEC$ ATTRIBUTES ALIAS:"TA_GetFeatureValue" :: TA_GetFeatureValue
What am I missing?
First thing to do here is determine the actual global name of the routine. Take the .lib from the DLL build and do:
dumpbin -exports mydll.lib
You should not need to add the leading underscore - BIND(C) will do that for you.
When you added the underscore to the ATTRIBUTES directive, you effectively removed the directive as you're now changing the attribute of a different routine.
It's possible that the new DLL isn't following the C compiler's conventions for routine naming - evidence being that you can link without it. If that is the case, you can't use BIND(C) and must revert to the older way of:
!DIR$ ATTRIBUTES STDCALL,REFERENCE,ALIAS:"_TA_GetFeatureValue" :: TA_GetFeatureValue
You will also need to add a !DIR$ ATTRIBUTES VALUE :: cchValue to the interface block.
Thanks, Steve. That did the trick. There are of course other routines, and once I gave all necessary variables the VALUE attribute, it works great.
I've attached a dumpbin. It shows only two lines containing GetFeatureValue.
15 E 00023464 GetFeatureValue 36 23 00023272 TA_GetFeatureValue
In Dependency Walker I see these two along with a third; _TAGetFeatureValue@16. Does this mean the DLL isn't following the C compiler's conventions for routine naming?
It's a mystery to me, too.
It may not be important, but I mis-stated what was in the dumpbin. I should have said:
15 E 00023464 GetFeatureValue 36 23 00023272 TA_GetFeatureValue 66 41 00023464 _GetFeatureValue@12 87 56 00023272 _TA_GetFeatureValue@16
What I now have for a typical interface block is as follows.
function TA_GetFeatureValue(handle, featureName, lpValueStr, cchValue) result(retval) !DIR$ ATTRIBUTES STDCALL,REFERENCE,ALIAS:"_TA_GetFeatureValue" :: TA_GetFeatureValue !DIR$ ATTRIBUTES VALUE :: handle, cchValue import integer(c_int32_t), value :: handle integer(c_short), dimension(*) :: featureName integer(c_short), dimension(*) :: lpValueStr integer(c_int), value :: cchValue ! Note the value attribute here integer(c_long) :: retval ! HRESULT is typedef long end function
So far it works. Is this the right way?
Different. STDCALL affects procedure calls on 32-bit only. It has to do with who pops the argument list off the stack. With STDCALL, the called routine does it in the RET instruction; with C, the caller does it. This is why you get stack corruption if you mix them.
Steve - do you have any ideas why the STDCALL directive was not adequate? Could it have something to do with the 4 different flavors of the routine identified by the dumpbin? Should I report back to the developer of this DLL that there is something odd with it?
If I may be brutally honest, I don't believe you tested the STDCALL directive correctly. The evidence from the dump of the exports shows that the symbol you reported as missing is actually there. Perhaps you linked to the wrong library - I don't know. That there is something "odd with the DLL" would be far down my list.
If you can attach a ZIP of a reproducible test case (Fortran source and export LIB - don't need the DLL), I'd be happy to take a look.
Being honest is not brutal.
As I reported earlier, the following gives me unresolved external linker error.
!DIR$ ATTRIBUTES STDCALL :: TA_GetFeatureValue
Whereas the following gets rid of the linker error, but when the program finishes running I got an error about corrupting the stack. But Steve said earlier an underscore should not be required because BIND C should do that.
!DIR$ ATTRIBUTES STDCALL :: _TA_GetFeatureValue
Anyhow, today I'm not getting the error about the stack, but I still need to add the underscore.
In the attached zip, open LimeMod stdcall.f90 and go to line 46. If the underscore is removed, I get unresolved external. With the underscore, the code builds and runs, but strangely doesn't corrupt the stack like it did for me a few days ago.
Ok, I see we have a misunderstanding here. It doesn't help that the compiler isn't alerting you about it.
function TA_GetFeatureValue(handle, featureName, lpValueStr, cchValue) result(retval) bind(c, name = 'TA_GetFeatureValue') !DIR$ ATTRIBUTES STDCALL :: _TA_GetFeatureValue
What you did here was tell the compiler that a routine named _TA_GetFeatureValue is STDCALL. Great, except that's not the name of the routine you're declaring an interface to. The result is that this directive is silently ignored.
I constructed a minimal example to demonstrate this. Here's the result of building with the directive the way you had it:
test.obj : error LNK2019: unresolved external symbol _TA_GetFeatureValue referenced in function _MAIN__ test.exe : fatal error LNK1120: 1 unresolved externals
(not linking to a library)
and here's with the underscore in the directive removed:
test.obj : error LNK2019: unresolved external symbol _TA_GetFeatureValue@16 referenced in function _MAIN__ test.exe : fatal error LNK1120: 1 unresolved externals
Note that in both cases the compiler added the leading underscore, and with the correct name in the directive also added the @16 for STDCALL.
Another thing I see in the dumpbin output is that the C++ compiler (I think that's what built the library) has both _TA_GetFeatureValue and _TA_GetFeatureValue@16 aliased to the same entry point. So it should not matter which of these you call - as long as the caller knows to apply the STDCALL mechanism. When you incorrectly add the leading underscore to the procedure name in the directive, the compiler thinks it is using the C mechanism and the stack gets corrupted by popping an extra 16 bytes from the stack.
The insidious thing about stack corruption is that it isn't always immediately obvious - it all depends on what happens after that.
Now I am going to link my test program to your library (with the errant underscore removed.) Let's see what I get:
test.obj : error LNK2019: unresolved external symbol _TA_GetFeatureValue@16 referenced in function _MAIN__
So that's what you see. Fine. Now let's look at the dumpbin.txt you included:
Dump of file turboactivate.DLL File Type: DLL Section contains the following exports for TurboActivate.dll 00000000 characteristics FFFFFFFF time date stamp 0.00 version 1 ordinal base 106 number of functions 106 number of names ... 87 56 00023272 _TA_GetFeatureValue@16
Looks reasonable. But wait.. it's a dump of the DLL, not the .LIB. What do we get if we dump the .LIB? It's short so I'll include the whole thing:
Dump of file turboactivate.lib File Type: LIBRARY Exports ordinal name ?DeleteProductKey@@YAXI@Z (void __cdecl DeleteProductKey(unsigned int)) ?GetPPDetails@@YAJPAUPartialProductDetails@@@Z (long __cdecl GetPPDetails(struct PartialProductDetails *)) ?SetProgressDelegate@@YAXP6AXH@Z@Z (void __cdecl SetProgressDelegate(void (__cdecl*)(int))) _Activate _ActivateEx _ActivateFromFile _ActivationRequestToFile _ActivationRequestToFileEx _CheckAndSavePKey _Deactivate _DeactivationRequestToFile _ExtendTrial _GetCurrentProduct _GetExtraData _GetFeatureValue _GetPKey _IsActivated _IsDateValid _IsGenuine _IsGenuineEx _IsProductKeyValid _PDetsFromPath _SetCurrentProduct _SetCustomActDataPath _SetCustomProxy _TA_Activate _TA_ActivateFromFile _TA_ActivationRequestToFile _TA_CheckAndSavePKey _TA_Cleanup _TA_Deactivate _TA_DeactivationRequestToFile _TA_ExtendTrial _TA_GenuineDays _TA_GetExtraData _TA_GetFeatureValue _TA_GetHandle _TA_GetPKey _TA_GetVersion _TA_IsActivated _TA_IsDateValid _TA_IsGenuine _TA_IsGenuineEx _TA_IsProductKeyValid _TA_PDetsFromByteArray _TA_PDetsFromPath _TA_SetCustomActDataPath _TA_SetCustomProxy _TA_SetTrialCallback _TA_TrialDaysRemaining _TA_UseTrial _TA_UseTrialVerifiedFromFile _TA_UseTrialVerifiedRequest _TrialDaysRemaining _UseTrial
What's missing? My guess is that this is a 64-bit .LIB, naturally missing all of the STDCALL entry points. Because the DLL itself is 32-bit, you would not notice the mismatch on running.
The solution is to 1) not put a leading underscore on the procedure name in the directive and 2) link against the correct export library.
What you did here was tell the compiler that a routine named _TA_GetFeatureValue is STDCALL. Great, except that's not the name of the outine you're declaring an interface to. The result is that this directive is silently ignored.
Hmmm not very user friendly. It begs the question why the STDCALL needs a name at all when in a bind(c name= is specified. In gfortran the stdcall decl is specified for the actual Fortran name rather the the alias which also makes more sense.
I'm not being clear here, it seems.
The STDCALL directive applies to the Fortran procedure name, NOT the alias. You are changing an attribute of the name as referenced in your Fortran code. This should be the same as gfortran.
When you use BIND(C, NAME=), the standard says that any "decoration" of the name should match what the "companion C processor" would do. And it does - MSVC adds the leading underscore and the @16 suffix when you tell it that a function is __stdcall.
You need the STDCALL in Fortran because this affects the generated code. The default (for both ifort and MSVC) is the "C" mechanism, where the caller pops the stack after return. STDCALL has the called procedure do the popping, but this then means that the called procedure has to know exactly how many bytes to pop. To help avoid mismatches, the convention is to append @n to the procedure name, where n is the number of bytes.
What you are seeing is that MSVC is ALSO emitting a name without the suffix - and Intel Fortran does this too. Why? Fortran does it because that allows you to declare a name simply EXTERNAL and pass it as an argument. I am not sure why MSVC is doing it as well.
I forgot to note that you made the same error with the directive for several other procedures you declared in this source.
As for why the compiler doesn't warn you that you gave a STDCALL attribute to a name you never used - doing so would probably annoy other users who include a lot of declarations for routines not currently used.
I also retract my guess that the .LIB is 64-bit. Rather, I think it is from a CDECL (C mechanism) version of the DLL, as the names all have leading underscores, and the convention on x64 is that this is omitted.
Why do forum posts not have identifying numbers on them anymore?
Steve, I believe you said in your minimal example, linking failed both with & without the underscore. I only get a linker error without the underscore, and with it links ok.
I can confirm that the .lib file is indeed 32 bit. I have another that is 64 bit. A dumpbin from the 32 has underscores, and the 64 doesn't.
The creator of these files gives me DLL files in cdecl and stdcall flavors. But only one .lib file. Not knowing any better, I took it for granted that one .lib file works for both. If stdcall requires a different .lib file, let me know and I'll ask them for it.
If you are wondering why I want to use the stdcall flavor. I call this DLL from Excel visual basic as well as fortran. So I want to use the same DLL for both.
I found a google page that shows Intel's MKL has DLL's on both flavors, and the LIB files are different. So I have asked my supplier for a proper stdcall-LIB file.
If I got it right (always doubtful), a "correct" directive will not have an underscore at the front of the name, and BIND(C, name='xyz') will be in the declaration, and I won't need any !DIR$ ATTRIBUTES VALUE statements.