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

Calling a dll made with Fortran by C

patuco
Beginner
7,020 Views
Hi,
i am getting crazy tying to find out where or why is teh problem in the program I'm doing. If someone could help me, it would be great.
Ill explain you my problem:
i am doing some test to try out how the jni of javaworks. i implemented a dumy program which only purpose is to call a Frotran dll which has to show on the screen the typical Hello world. I did the C++ wrapper using the visual studio .net. Ive already accomplished the communication between the wrapper and Java. in a second stage I try to do the same but with the wrapper calling the fortran dll, and here is where the problems occur: the function called by the wrapper located in the fortran dll is not "found":
ReadValue error LNK2019: smbolo externo _PRINTER@0 sin resolver al que se hace referencia en la funcin "long __stdcall Java_ReadValue_getValue(struct JNIEnv_ *,class _jobject *)" (?Java_ReadValue_getValue@@YGJPAUJNIEnv_@@PAV_jobject@@@Z)
The Fortran code is as follows:
INTEGER FUNCTION PRINTER()
!decs attributes c :: PRINTER
!MS$ATTRIBUTES DLLEXPORT,STDCALL,ALIAS:'PRINTER'::PRINTER
WRITE(6,'("-- FORTRAN funcion printer --")')
PRINTER=2
END
as you can observed is quite obvious.
So as the wrapper:

#include "stdafx.h"

#include "stdio.h"

#include "ReadValue.h"

#include "jni.h"

extern "C" __stdcall int PRINTER();

// i even try with : extern "C" __cdecl int PRINTER();

JNIEXPORT jint JNICALL Java_ReadValue_getValue(JNIEnv * env, jobject jobj)

{

printf("-- A ver si funciona la referencia -- ");

int result = PRINTER();

printf(" -- valor de retorno ke leyo del archivo -- ",result);

return result;

}

Anyone can help me?? Thanks.

0 Kudos
78 Replies
patuco
Beginner
1,547 Views

Hi,

i have a problem, i should be doing something terrible because i.m getting an exception from the java virtual machine. Im trying the dll to return to the java a String so im doing like follows:

wrapper:

JNIEXPORT jstring JNICALL Java_Main_get_1name(JNIEnv *env, jobject jobj, jint param)

{

char buf[80];// = (char*)malloc(num_chars);

strcpy(buf, Getname(param +1));

return js;

and the fortran:

CHARACTER(80) FUNCTION GETNAME(VALUE)
!MS$ATTRIBUTES DLLEXPORT,C,ALIAS:'_Getname'::GETNAME
INCLUDE 'COMMON.LVG'
INTEGER VALUE
WRITE(*,*) "The VALUE is ", MOL(VALUE)
GETNAME=MOL(VALUE)
END FUNCTION

and the error comes where the fortran call is located. :?

# An unexpected error has been detected by HotSpot Virtual Machine:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x02cb4c7c, pid=2804, tid=1748
#
# Java VM: Java HotSpot Client VM (1.5.0_06-b05 mixed mode, sharing)
# Problematic frame:
# C [mlvg.dll+0x14c7c]
#
# An error report file with more information is saved as hs_err_pid2804.log
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
#

Anyone could give me a clue? Thanks.

Message Edited by patuco on 02-07-2006 01:17 AM

0 Kudos
Steven_L_Intel1
Employee
1,547 Views
I am absolutely certain that what Java wants to see for a function returning a string isn't what Fortran provides.

In Intel Fortran, a function that returns a CHARACTER value has the return variable passed as a hidden first argument, as if it were a subroutine with an extra character argument. That also affects where the string lengths are.

Also, do you know for certain that Java wants to call routines using the C calling mechanism and not STDCALL?
0 Kudos
patuco
Beginner
1,547 Views
hi,
i'm sure i need the c cuz the test i did showing on the screen the value worked. It is something related with the returning value, i will try what Jugoslav suggested days befor, passing the array as parameter, but my goal was trying to avoid it.
0 Kudos
patuco
Beginner
1,547 Views

and of course what java expect is not what fortran provides... but that is one of the purpose of the wrapper: it receives a char * from Fortran and it will transform it into an String for Java.

The point is that ive never used the type CHAR(80), is something i found looking through the help, so i do not know how can i implement a function in fortan which returns a variable which contains only characters.

0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,547 Views
subroutine GetName(name)
!DEC$ATTRIBUTES whatever
CHARACTER(*) name
name = ...
name = trim(name)//char(0)
end subroutine GetName

extern "C" Getname(char*, int)
...
Getname(buf, 80)

0 Kudos
patuco
Beginner
1,547 Views

Hi everyone,

Ive been trying to copy the string in the fortran code (it is equivalent as C123TE) in the wrapper but I could accomplish it.

The wrapper calls the native as follows:

Declaration

extern "C" __declspec(dllexport) char* Getname(char *,int );//_Getname

Implementation

JNIEXPORT jstring JNICALL Java_Main_get_1name(JNIEnv *env, jobject jobj, jint param)

{

char *buf = (char*)malloc(80);

buf = " ";

printf("Before calling the wrapper: %s ",buf);

//char buf[80];

printf(" --C Calling the native ");

Getname(buf, param);

printf("%s ",buf);

printf("-- C Return form the native->buf: ", buf);

return env->NewStringUTF(buf);

}

The fortran is as follows (names of the variables are unsignificant):

Mol is an array of chars which combines signs and numbers with values like: CE23R, O89R, R4576,23R45.

SUBROUTINE GETNAME(ARRAKY, VALUE)

!MS$ATTRIBUTES DLLEXPORT,C,ALIAS:'_Getname'::GETNAME INCLUDE ''COMMON.LVG'

INTEGER VALUE

CHARACTER*(*) ARRAKY

WRITE(*,*) "The INPUT is ", ARRAKY

WRITE(*,*) "The FORTRAN is ", MOL (VALUE)

ARRAKY= MOL (VALUE)

ARRAKY = TRIM(ARRAKY)

WRITE(*,*) "The ARRAKI is ", ARRAKY

END SUBROUTINE GETNAME

Output:

Before calling the wrapper:

--C -- Calling the native

The INPUT is h

The FORTRAN is C17B

The ARRAKI is C

-- C Return form the native:

As you can observed only the first character is copied. :?

I`ve tried using an auxiliar char* and using a line like :

AYU(1:10)=MOL(VALUE)(1:10)

being the string copied into AYU ok but not in one passed as parameter:

ARRAKY(1:10)=AYU(1:10)

so as in the wrapper defining a static array (buffer[80]) but getting the same results.

Do you have any idea or suggestion? Thanks.

Message Edited by patuco on 02-07-2006 01:15 AM

Message Edited by patuco on 02-07-2006 01:18 AM

0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,547 Views
When a string argument appears in Fortran code, it is "transcribed" as two arguments in C. One is char* (argument address) and the other is int (buffer length). By default, the latter is passed at the end of argument-list.

So, for the given Fortran routine, the C prototype should be:
extern "C" __declspec(dllexport) 
char* Getname(char* string, int value, int stringlen);

Stringlen should be 80 in this case, obviously. Also, you should append a char(0) to the Fortran string, so that it can be parsed correctly in C:

ARRAKY= TRIM(MOL(VALUE))//CHAR(0)
0 Kudos
patuco
Beginner
1,547 Views

hi,

ive tryed your suggestions but i do not get what i am not considering,because the same behaviour is produce, i mean, the fortran code is parse:

SUBROUTINE GETNAME(ARRAKY, VALUE)
!MS$ATTRIBUTES DLLEXPORT,C,ALIAS:'_Getname'::GETNAME
c!MS$ATTRIBUTES REFERENCE :: ARRAKY
INCLUDE 'COMMON.LVG'
INTEGER VALUE
CHARACTER*(*) ARRAKY
WRITE(*,*) "The INPUT is ", ARRAKY
WRITE(*,*) "The FORTRAN is ", MOL(VALUE)
ARRAKY= TRIM(MOL(VALUE))//CHAR(0)
WRITE(*,*) "The ARRAKI is ", ARRAKY
END SUBROUTINE GETNAME

being call in the way:

extern

"C" __declspec(dllexport) char* Getname(char * ,int ,int );

//string, str_length, element

JNIEXPORT jstring JNICALL Java_Main_get_1name(JNIEnv *env, jobject jobj, jint param)

{

char *buf = (char*)malloc(80);

buf = " ";

Getname(buf, 80,80);

printf("%s",buf);

//strcpy(buf, Getname(param,aux));

return env->NewStringUTF(buf);

}

but the results arelike before:

The INPUT is h //???
The FORTRAN is OH
The ARRAKI is O //?????


Message Edited by patuco on 02-07-2006 01:18 AM

0 Kudos
patuco
Beginner
1,547 Views

Adding some comments to my previous subject is that i was performing some tests and getting all teh time the same result, only the first character is copied:

extern

"C" __declspec(dllexport) char* Getname(int * value, char * string, int * stringlen);//_Getname

char
*buf = (char*)malloc(10);

Getname(&parameter, buf, &i);//parameter = i = 10

SUBROUTINE GETNAME(VAL, ARRAKY)
!MS$ATTRIBUTES DLLEXPORT,C,ALIAS:'_Getname'::GETNAME
CHARACTER (*)ARRAKY
ARRAKY="ayudaaa"
WRITE(*,*) "The INPUT is? ", ARRAKY
END SUBROUTINE GETNAME

And another point, after leaving the fortran code the value is not saved (for the moment only one character)!!!!! I thought iwas passing the parameter by reference.

Message Edited by patuco on 02-07-2006 05:05 AM

0 Kudos
patuco
Beginner
1,547 Views
Adding to the last comment, after leaving the fortran dll the value of the char^* is not saved, when i thought i was passing it by reference ????
0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,547 Views
buf should go by reference, but not buflen. Remove the *.
0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,547 Views
...also, DEC$ATTRIBUTES C implies call-by-value for scalar arguments (but not for strings and arrays). !DEC$ATTRIBUTES C, REFERENCE implies call by reference. However, hidden string length is never passed by reference.
0 Kudos
patuco
Beginner
1,547 Views

before trying

extern "C" __declspec(dllexport) char* Getname(int *molecule, char * string, int *stringlen);//_Getname

i tryed with

extern

"C" __declspec(dllexport) char* Getname(int molecule, char * string, int stringlen);//_Getname

but getting the same result, on ly the first char is copied and what i found after leaving the fortran it is not actualized. Could it be the C decalaration, so instead of it i should used stdcall?

I am getting crazy.... i do not know what is going on with this char *???

By the way, thanks a lot for your help ok?

0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,547 Views
I forgot it's CVF 6.0, not IVF. In CVF, the string length comes immediately after string, not at the end of the list. Thus,

extern "C" __declspec(dllimport) void
Getname(int molecule, char* string, int stringlen);//_Getname

is paired with

subroutine Getname(molecule, string)
!DEC$ATTRIBUTES C, DLLEXPORT, ALIAS:"_Getname":: Getname
integer molecule
character(*) string

WRITE(*,*) LEN(string)
0 Kudos
patuco
Beginner
1,547 Views

I tryed your suggestion and i get a result which makes mescare.

Output

1
he INPUT MOLECULE is? |

Using only LEN to obtain the length of the array passed is only 1 !!??

i think the parameter definition and implementation is ok, isnt it?

extern

"C" __declspec(dllimport) void Getname(int molecule, char * string, int stringlen);

Implementation

int i = 15;//length of the vector

int parameter = 5;//to access the 5th element in fortran

char *buf = (char*)malloc(i);

buf = " ";

printf("--C-- buffer: %s ",buf);

Getname(parameter, buf, i);

printf(" -- C --REturned buf: %s ", buf);

jstring jstrBuf = env->NewStringUTF(buf);

return jstrBuf;

by the way, why instead fo dllexport, dllimport??

i tryed another test and the same

SUBROUTINE GETNAME(VAL, ARRAKY)
!MS$ATTRIBUTES DLLEXPORT,C,ALIAS:'_Getname'::GETNAME
CHARACTER (*)ARRAKY
ARRAKY="ayudaaa"
ARRAKY= TRIM(ARRAKY)//CHAR(0)
WRITE(*,*) LEN(ARRAKY)
write(*,*) "The INPUT MOLECULE is? ", ARRAKY
END SUBROUTINE GETNAME

output:

1
The INPUT MOLECULE is? a

0 Kudos
patuco
Beginner
1,547 Views

adding to the last comment where i explain the results obtained

i am trying another thing using de atributes value and reference, as follows:

SUBROUTINE GETNAME(VAL, ARRAKY)
!MS$ATTRIBUTES DLLEXPORT,C,ALIAS:'_Getname'::GETNAME
!MS$ATTRIBUTES VALUE :: VAL
!MS$ATTRIBUTES REFERENCE :: ARRAKY
CHARACTER *48 ARRAKY
cARRAKY="ayudaaa"
cARRAKY= TRIM(ARRAKY)//CHAR(0)
WRITE(*,*) LEN(ARRAKY)
write(*,*) "The INPUT MOLECULE is? ", ARRAKY

now the output of the length is what i specified in the declaration !!????

and another thing is that each time i tryed to copy values im gettin an error.

0 Kudos
patuco
Beginner
1,547 Views

eventhough defininig the buffer as :

const int i = 15;

char buf;

the output is the same as i commented:
--C--buffer:
--C --Calling native
1
The INPUT MOLECULE is? ?
-- C --REturned buf:
0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,547 Views
The next-to-last version looks OK. You shouldn't hard-code string length in Fortran, as in the last version that "overrides" the length passed by caller, and you can easily end up overwriting something that you shouldn't.

Sorry, I can't spot anything wrong with the next-to-last version.
0 Kudos
patuco
Beginner
1,547 Views
which of the versions are you talking about? i paste a couple and i don`t get why all the time the length of the parameter is 1
0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,544 Views
Never mind. My bad I thought that !DEC$ATTRIBUTES C does not affect how string arguments are passed; however, it does... grrr...

It appears that !DEC$ATTRIBUTES C also implies that strings are passed by value (!). I don't know why on Earth would anyone want to do that, but that's how it's implemented.

So, you should specify REFERENCE for the string argument, and pass the length explicitly:

 SUBROUTINE GETNAME(VAL, ARRAKY, iLen)
!MS$ATTRIBUTES DLLEXPORT,C,ALIAS:'_Getname'::GETNAME
!MS$ATTRIBUTES REFERENCE:: Arraky
INTEGER val
CHARACTER(iLen) ARRAKY
ARRAKY="ayudaaa"
ARRAKY= TRIM(ARRAKY)//CHAR(0)
WRITE(*,*) LEN(ARRAKY)
write(*,*) "The INPUT MOLECULE is? ", ARRAKY
END SUBROUTINE GETNAME

extern "C" __declspec(dllimport) void
Getname(int molecule, char* string, int len);

Btw, __declspec(dllimport) is needed (but can be omitted) because you're importing the symbol from another dll.
0 Kudos
Steven_L_Intel1
Employee
1,544 Views
MS did it that way so DVF followed suit.
0 Kudos
Reply