- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
From fortran I want to call a routine in a C DLL that takes a string argument. Is there a web page that explains how to do this? The C DLL uses stdcall calling convention. In the header file for the DLL the string argument appears to be declared as (const void *).
In a C program that calls this DLL, the string argument which is passed is created with a statement as follows:
unsigned char str_argument[] ="blahblahblah"
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The calling convention doesn't matter here.
If you can use BIND(C), declare the dummy argument as:
CHARACTER, DIMENSION(*)
You can pass an arbitrary-length Fortran string to this. Make sure the string you pass is NUL-terminated.
If you're not using BIND(C), then use ATTRIBUTES directives to give both the routine AND the dummy argument the REFERENCE attribute.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
No luck so far. The code builds and runs, but the function call is not succeeding. vendor_code is the string argument giving me trouble.
In the interface block
function hasp_login(feature_id, vendor_code, handle) !DEC$ ATTRIBUTES STDCALL :: hasp_login import integer(c_int32_t) :: feature_id integer(c_int32_t) :: handle character, dimension(*) :: vendor_code integer(c_long) :: hasp_login end function
In the main program.
USE, INTRINSIC :: ISO_C_BINDING integer(c_int32_t) handle, featureID integer status character(len=984,kind=C_CHAR):: vendor_code vendor_code = "blahblahblah" // C_NULL_CHAR status = hasp_login(6, vendor_code, handle)
If the vendor_code were simply wrong, there is a status return value to indicate that. But I'm getting a status saying "invalid function parameter". So I don't think the argument is being passed correctly.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Pardon my frustration, but you ignored almost every single bit of advice I gave you. I hope you're not asking me to write your code for you.
Please go back and read my previous reply.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I would expect the interface requires the reference attribute for vendor_code. I think hasp_login is being passed back so it needs to be reference also unless you are meant to pass LOC(handle) by value. You need to look at the hasp_login API doc or a C language sample call to see what is needed.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I very much appreciate the help. What I'm trying to do is over my head, and I have to confess even the suggestions over my head. I am more than willing to hire someone to do this for me. Any takers? Go to www.xlrotor.com and use the email link at the bottom.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Brian Murphy wrote:.. What I'm trying to do is over my head, and I have to confess even the suggestions over my head. I am more than willing to hire someone to do this for me. Any takers? ..
@Brian Murphy,
You don't provide sufficient details on the 'hasp_login' function in the C DLL in question. Suppose for a moment the prototype for this function and its implementation is as follows:
#include <stdio.h> // Function prototype __declspec(dllexport) int __stdcall hasp_login(int *, const void *, int *); int __stdcall hasp_login(int *feature_id, const void *vendor_code, int *handle) { printf("In C function hasp_login:\n"); printf("vendor_code is %s\n", (char *)vendor_code); printf("feature_id: %d\n", *feature_id); printf("handle: %d\n", *handle); return 0; }
Then, if you intend to employ the Intel Fortran (and previously with DEC Fortran) compiler-based non-standard options toward mixed-language programming, you can write your Fortran code to make use of above C function as follows:
interface function hasp_login(feature_id, vendor_code, handle) result(r) !DIR$ ATTRIBUTES STDCALL, REFERENCE, DECORATE :: hasp_login !DIR$ ATTRIBUTES REFERENCE :: vendor_code integer, intent(inout) :: feature_id character(len=*), intent(in) :: vendor_code integer, intent(inout) :: handle integer :: r end function end interface integer :: fid, hwnd, irc character(len=:), allocatable :: vc fid = 1 ; hwnd = 2 vc = "BLAHBLAHBLAH" // char(0) irc = hasp_login(fid, vc, hwnd) end
Now, you can test the above for yourself by compiling and linking the C function into a DLL and for a Fortran program to dynamically link to it and produce the expected output by following the steps below in a Windows command prompt corresponding to your Intel Fortran compiler version for IA32 environment:
C:\Temp>type c.c #include <stdio.h> // Function prototype __declspec(dllexport) int __stdcall hasp_login(int *, const void *, int *); int __stdcall hasp_login(int *feature_id, const void *vendor_code, int *handle) { printf("In C function hasp_login:\n"); printf("vendor_code is %s\n", (char *)vendor_code); printf("feature_id: %d\n", *feature_id); printf("handle: %d\n", *handle); return 0; } C:\Temp>cl /c /W3 /EHsc c.c Microsoft (R) C/C++ Optimizing Compiler Version 19.23.28106.4 for x86 Copyright (C) Microsoft Corporation. All rights reserved. c.c C:\Temp>link c.obj /DLL /out:c.dll Microsoft (R) Incremental Linker Version 14.23.28106.4 Copyright (C) Microsoft Corporation. All rights reserved. Creating library c.lib and object c.exp C:\Temp>type p.f90 interface function hasp_login(feature_id, vendor_code, handle) result(r) !DIR$ ATTRIBUTES STDCALL, REFERENCE, DECORATE :: hasp_login !DIR$ ATTRIBUTES REFERENCE :: vendor_code integer, intent(inout) :: feature_id character(len=*), intent(in) :: vendor_code integer, intent(inout) :: handle integer :: r end function end interface integer :: fid, hwnd, irc character(len=:), allocatable :: vc fid = 1 ; hwnd = 2 vc = "BLAHBLAHBLAH" // char(0) irc = hasp_login(fid, vc, hwnd) end C:\Temp>ifort /c p.f90 Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on IA-32, Version 19.0.5.281 Build 20190815 Copyright (C) 1985-2019 Intel Corporation. All rights reserved. C:\Temp>ifort p.obj c.lib /exe:p.exe Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on IA-32, Version 19.0.5.281 Build 20190815 Copyright (C) 1985-2019 Intel Corporation. All rights reserved. Microsoft (R) Incremental Linker Version 14.23.28106.4 Copyright (C) Microsoft Corporation. All rights reserved. -out:p.exe -subsystem:console p.obj c.lib C:\Temp>p.exe In C function hasp_login: vendor_code is BLAHBLAHBLAH feature_id: 1 handle: 2 C:\Temp>
Note the same principle applies in terms of your Fortran code and the !DIR$ ATTRIBUTES that need to be used if you're employing Visual Studio 2010 solution(s) and project(s) in your work. I'll leave it up to you as exercise to try out such a unit test in case you continue to face issues.
Hope this helps,
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
There is a reasonable example at this link, although FF has provided most of what you need.
I forget FortranFan on the list this morning -- my apologies.
PS: overall lesson for the day - do not give a sick 12 year old a bell to call you when she is sick -- it becomes Pavlovian
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
@Brian Murphy,
Since you suggest in this other thread (https://software.intel.com/en-us/forums/intel-fortran-compiler/topic/842768) you're back to using C interoperability features in standard Fortran, you can consider the modified code below instead for the Fortran program in Quote #7:
use, intrinsic :: iso_c_binding, only : c_int, c_char, c_null_char interface function hasp_login(feature_id, vendor_code, handle) result(r) bind(C, name="hasp_login") !DIR$ ATTRIBUTES STDCALL :: hasp_login import :: c_int, c_char integer(kind=c_int), intent(inout) :: feature_id character(kind=c_char,len=1), intent(in) :: vendor_code(*) integer(kind=c_int), intent(inout) :: handle integer(c_int) :: r end function end interface integer(c_int) :: fid, hwnd, irc character(kind=c_char, len=:), allocatable :: vc fid = 1 ; hwnd = 2 vc = "BLAHBLAHBLAH" // c_null_char irc = hasp_login(fid, vc, hwnd) end
You can try to repeat the steps shown in Quote #7 and get the same output from the program i.e., assuming you are using a reasonably recent version of Intel Fortran compiler that added support for STDCALL convention on Windows in conjunction with C interoperability features.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I can't thank you enough for the help. I have installed vs2015 and composer 2018, and also have paid for license update/renewal to the latest versions, but I can't get the latest version until Intel gets my registration problems fixed, and that's proving to be a tough nut to crack.
I hope to report back that I have it working, but give me a few days.
In the meantime, the C function prototype is complicated by a chain of include files and (to me) uncertain data types. I will try to pick out the relevant bits from a working C console program.
enum hasp_error_codes { HASP_STATUS_OK = 0, lots more of these }; #define HASP_CALLCONV __stdcall typedef unsigned long hasp_u32_t; typedef enum hasp_error_codes hasp_status_t; typedef hasp_u32_t hasp_size_t; typedef hasp_u32_t hasp_handle_t; typedef hasp_u32_t hasp_feature_t; typedef const void *hasp_vendor_code_t; Here is the C prototype exactly as given in a working C console program. hasp_status_t HASP_CALLCONV hasp_login(hasp_feature_t feature_id, hasp_vendor_code_t vendor_code, hasp_handle_t *handle); My attempt at substituting leads me to this, where I have guessed a C enum becomes an integer in fortran. Still not clear what to do with unsigned longs in fortran, and one appears to be passed by value and one by reference. integer __stdcall hasp_login(unsigned long feature_id, const void *vendor_code, unsigned long *handle);
Again, give me a few days and I hope to have this working.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The hasp_login function return is an error code/success indicator and the handle is a return argument, thus passing in pointer to handle (reference on Fortran side). It is unclear as to if vendor_code is a supplied or return value. You should consult the hasp_login documentation as to the arguments. On the C side, the *ointers are usually references (exception as to when the called function expects an opaque object that is not directly used, possibly saved and returned later).
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The vendor_code is a supplied parameter (const attribute in C interface) which identifies uniquely the owner/seller company of the software application.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hmmm. I could'a sworn I posted a reply last night which is now gone. I'll try again.
With Steve's help in the other thread, a VALUE attribute for the feature_id argument did the trick.
Elsewhere, some of the C functions return a string in an argument, and I have this working, too. All I did for that was change intent(in) to intent(inout). The returned string is null terminated, but it's length is not returned. Is the following the recommended way to get that: length = INDEX(strvble, c_null_char)-1
About the vendor_code. As #12 says, that is an input, so intent(in).
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I'll put in my usual alert about VALUE (the standard attribute, not what goes in a directive).
VALUE changes its meaning depending on whether the procedure has BIND(C) or not. If it is interoperable (BIND(C)), VALUE means that the argument is compatible with a C parameter that is received by value (doesn't have a * in the prototype.) But if BIND(C) is not used, VALUE means that an anonymous, writable copy of the argument is passed (by reference, usually, though the standard doesn't explicitly say that.)
Early Intel implementations of VALUE got this wrong, which why /assume:std_value exists.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page