- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Step 1. In VC++, I have something like this:
extern "C" {
__declspec(dllexport) void __stdcall WriteLogEntry
(char* appName, int appNameLen,
char* logName, int logNameLen,
char* message, int messageLen,
int typeID, int eventID, int catID );
}
Step 2. After creating the corresponding DLL and LIB, I followed some recommendations from here, namely the thing with "dumpbin -exports dllname.lib", which resulted in following:
Microsoft COFF/PE Dumper Version 9.00.21022.08
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file dllname.lib
File Type: LIBRARY
Exports
ordinal name
_WorkingDirectory@8
_WriteLogEntry@36
Summary
A8 .debug$S
14 .idata$2
14 .idata$3
4 .idata$4
4 .idata$5
16 .idata$6
Step 3. Finally, in my IVF code, I used
*dec$ attributes stdcall, dllimport, alias: "_WriteLogEntry@36" :: WriteLogEntry
The most important thing solving all problems with unresolved externals linker error was to use the alias from the dumpbin output.
Of course, the dllname.lib file has to be added to the project and also specified in project properties (Configuration Properties - Linker - Input - Additional Dependencies) as an additional dependency.
I hope that this might help sombody dealing with a similar problem in the future.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
*dec$ attributes stdcall, dllimport, decorate, alias: "WriteLogEntry" :: WriteLogEntry
Your C++ routine expects the string lengths to immediately follow the string addresses. That is not how Intel Fortran does it by default - you can specify that with the MIXED_STR_LEN_ARG attribute (preferable) or /iface:mixed_str_len_arg.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
*dec$ attributes stdcall, dllimport, decorate, alias: "WriteLogEntry" :: WriteLogEntry
I started getting a linker error: Only a function or subroutine subprogram may have the !DEC$ ATTRIBUTES directive DECORATE specifier. [WRITELOGENTRY]
I am going to add the MIXED_STR_LEN_ARG attribute after resolving the issue with the DECORATE specifier.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Using BIND rather than DEC$ ATTRIBUTES looks preferable since it directly expresses the intention: that you want to match the C compiler's linkage conventions.
I've started converting to BIND whenever I have to touch one of those interfaces. There's a new error message in the beta IVF 11 compiler that breaks when you use BIND on common blocks in conjunction with EQUIVALENCE so there are cases where you can't replace a nonportable directive with a semi-portable one.
On the other hand, the explicit alias breaks when you try to compile for 64-bit because the calling convention -- including the name decoration rules -- is different. That's what led me to discover the BIND alternative in the first place.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
As I have already mentioned, the C++ function is defined as:
extern "C" {
__declspec(dllexport) void __stdcall WriteLogEntry
(char* appName, int appNameLen,
char* logName, int logNameLen,
char* message, int messageLen,
int typeID, int eventID, int catID );
}
I am calling this function from Fortran in the following way:
call WriteLogEntry ( appName, appNamelen, logName, logNameLen, msg, msgLen, typeID, eventID, 0 )
where
character*30 appName
character*20 logName
integer*4 appNameLen, logNameLen, msgLen
character*1024 msg
integer*4 typeID, eventID
are initialized (an example)
appName = 'Application'
appNameLen = len_trim(appName)
logName = 'MyLog'
logNameLen = len_trim(logName)
etc.
I am still using
*dec$ attributes stdcall, dllimport, alias: "_WriteLogEntry@36" :: WriteLogEntry
because
*dec$ attributes stdcall, dllimport, decorate, alias: "WriteLogEntry" :: WriteLogEntry
did not work, see above.
I still have access to the C++ code, so I can change it to make things work. Would you have any example how to do it? I just need to pass few strings and integers to the C++ function and it seems that especially strings are more or less complicated.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Ah, thanks for the code example.
When the compiler passes the length, it does it automatically for you; it's a hidden argument not explicitly declared by you.
So, your example below:
call WriteLogEntry ( appName, appNamelen, logName, logNameLen, msg, msgLen, typeID, eventID, 0 )
is really passing three more arguments than you expected:
call WriteLogEntry ( appName, [hidden length], appNamelen, logName, [hidden length], logNameLen, msg, [hidden length], msgLen, typeID, eventID, 0 )
You should do two things. First - take out appNamelen, longNameLen, and msgLen.
Second, make sure that your strings are null-terminated. That's how C finds the "end" of the string.It's easy to do; change this:
appName = 'Application'
to this:
appName = 'Application'C
Hopefully this will help!
- Lorri
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
![Sad smiley [:(]](/isn/Community/en-US/emoticons/emotion-9.gif)
My understanding is that by using STDCALL in the statement
*dec$ attributes stdcall, dllimport, alias: "_WriteLogEntry@36" :: WriteLogEntry
all integers (typeID, eventID, appNameLen, logNameLen, msgLen) will be passed by value, right? Also, and it might be my problem, strings are passed in the following way: "the first character is converted to INTEGER(4) as in ICHAR(string(1:1)) and pushed on the stack by value". It is something I don't want, I want the whole string to be passed, so I need to pass them by reference, right? However, I don't know how to do that. If I add REFERENCE to the line shown above, also integers would be passed by reference and I would have to rewrite the C++ function in the DLL.
In addition, the default IVF settings determine that the hidden length arguments are passed after all arguments, which means that it is not causing my problems. I checked in debugger, that string lengths are passed correctly.
I have one more question: why is adding DECORATE or MIXED_STR_LEN_ARG to the line above causing the compilation to be aborted with "Error: Only a function or a subroutine subprogram may have the !DEC$ ATTRIBUTES directive DECORATE/MIXED_STR_LEN_ARG specifier." ? Am I missing something in my code?
subroutine WriteLogEntryF77 ( )
use ifport ! used for len_trim
*dec$ attributes stdcall, dllimport, alias: "_WriteLogEntry@36" :: WriteLogEntry ! not recommended
!*dec$ attributes stdcall, dllimport, decorate, alias: "WriteLogEntry" :: WriteLogEntry ! does not work with decorate
include 'EventLog.inc' ! strings and their lengths to be passed to C++ DLL
integer*4 msglen
integer*4 len_trim
msg = msg//char(0) ! //char(0) was added also to appName and logName
msgLen = len_trim(msg)
call WriteLogEntry ( appName, appNamelen, logName, logNameLen, msg, msgLen, typeID, eventID, 0 )
return
end
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
On the C++ side, I recommend you change the prototype to
extern "C" {
__declspec(dllexport) void __stdcall WriteLogEntry
(char* appName, int &appNameLen,
char* logName, int &logNameLen,
char* message, int &messageLen,
int &typeID, int &eventID, int &catID );
}
sending pointers (addresses) for all arguments, requiring that they be given REFERENCE attribute on the Fortran side.
__stdcall results in a leading underscoreand a trailing "@36" being added to the function name to create a symbol in the symbol table of ther DLL, hence_ WriteLogEntry@36 with case being preserved.
On the Fortran side, add an Interface block for your subroutine WriteLogEntry containing the directives
!DEC$ ATTRIBUTES STDCALL, ALIAS : '_WriteLogEntry@16' :: WriteLogEntry
!DEC$ ATTRIBUTES REFERENCE::appName,appNameLen, logName,logNameLen
!DEC$ ATTRIBUTES REFERENCE::message,messageLen, typeID, eventID,catID
and include it in files/modules wherever WriteLogEntry is referenced. Since C++ expects all character strings to be null-terminated as mentioned earlier, make sure you understand whether the length argument contains the null-character or not when using it on the C++ side. Also, since C++ uses the null character to sense the end of the string, you may be able to dispense with the string length argument when calling the C++ routine (as advised earlier). When passing strings the other way (from C++ to Fortran, I always include a pointer to the string length so I know how much storage to provide on the fortran side. In my experience, passing addresses appears to avoid confusion regarding hidden string lengths, since there appear to be none in that case.(Or, possibly, that is because I add iface:nomixed_str_len_arg to the Fortran project settings - I am not sure - it's a bit of a mystery!)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
No, you're not stupid; interoperability is a complicated area.
Yes, you are right - stdcall does unexpected things with strings. Sorry, I forgot about that (see! It's complicated!)
I think the best thing you can dois give the string arguments the REFERENCE attribute on the Fortran side
To do this, you will need an interface declaration on the Fortran side:
interface
subroutine WriteLogEntry ( appName,appNameLen, logName,logNameLen, msg,msg_len, typeID, eventID,otherID )
!dec$ attributes stdcall, dllimport, decorate, alias: "WriteLogEntry" :: WriteLogEntry
character*(*) appName
!dec$ attributes reference :: appName
character*(*) logName
!dec$ attributes reference :: logName
character(*) msg
!dec$ attributes reference :: msg
integer appNameLen, logNameLen, msg_len
integer typeID, eventID, otherID
end subroutine WriteLogEntry
end interface
Because the character strings are declared as REFERENCE, the hidden length is not being passed, and the argument count comes out as @36 (which is what you wanted). Since you want the lengths, however, you will have to pass them (but, you already do that anyway). It is not necessary to specify iface=mixed_str_len_arg
I believe the compiler complained about the other declarations because it didn't know you were referencing an external routine; by default it thought you were referencing a variable. At least, that's whatI think it was doing (I didn't chase it down)
I tried this with a tiny Fortran program calling a C (not C++) DLL, and got the results I expected.
Hope this helps -
- Lorri
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Good thing is that I did not have to change anything in C++ . I had to introduce an INTERFACE block which was the crucial step missing in my previous attempts. My code looks like this now:
interface
subroutine WriteLogEntry( appName, appNamelen, logName, logNameLen, msg, msgLen, typeID, eventID, catID )
!dec$ attributes stdcall, dllimport, decorate, alias: "WriteLogEntry" :: WriteLogEntry
! Event log entry parameters
character*30 appName
character*20 logName
character*1024 msg
integer*4 appNameLen, logNameLen, msgLen
integer*4 typeID, eventID, catID
!dec$ attributes reference :: appName, logName, msg
end subroutine WriteLogEntry
end interface
You can see that I am still using STDCALL (integers are thus passed by value) and I changed only the way how strings (appName, logName, msg) are passed, by reference, using the directive
!dec$ attributes reference :: appName, logName, msg
I agree with Anthony that passing all arguments by reference is clearer and easier to handle; unfortunately changing the C++ code would affect my colleagues' work which I was trying to avoid.
In addition, introducing the INTERFACE block resolved my problem with using DECORATE as described above. I have verified that everything works well.

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page