Calling VC++ functrion from a Fortran DLL

Calling VC++ functrion from a Fortran DLL

Hello everybody,

I am working on a Mixed language Program with VC++ and a fortran DLL. My Fortran DLL needs to call back a VC++(MFC) Function to store some value.

My VC++ function was...

void StoreOrtho(float x[600], float y[600],
float heightCoef[600], float slope[600]);

I call the function this way in fortran:

Call StoreOrtho(X,Y,HEIGHT_COEF,SLOPE)

But my main problem is that I don't know how to share this function to fortran. For sure I will have to declare My function as Extern, but how and where? And in My fortran subroutine how I said to her that StoreOrtho was a VC++ function.

Hope it was clear?

Demo_360

15 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

[pre]
SUBROUTINE MyDllSub(...,StoreOrtho)
!DEC$ATTRIBUTES DLLEXPORT:: MyDllSub
...
INTERFACE
SUBROUTINE StoreOrtho(x,y,hC,slope)
!DEC$ATTRIBUTES C:: StoreOrtho
REAL:: x(*),y(*),hC(*),slope(*)
END SUBROUTINE
END INTERFACE
...
Call StoreOrtho(X,Y,HEIGHT_COEF,SLOPE)
...
//====================================
#include "StoreOrtho.h"
//Fortran & callback function declaration
typedef void (*CALLBACKPROC)(
float* x, float* y, float* hC, float* slope);
extern "C" void _declspec(dllimport) __stdcall
MyDllSub(..., CALLBACKPROC Callback);
...
MyDllSub(...,StoreOrtho)
[/pre]

HTH
Jugoslav

Sorry but,

can you put some explanation around all this code, cause I'm a bit mixtup. I don't realy understand what you are doing.

Why did you write #include "StoreOrtho.h" in the C++ part. StoreOrtho function was part of a document Class call RefraDoc.

Thanks Demo_360

First, CALLBACKPROC is a typedef for (C) function of appropriate prototype. Last argument to MyDllSub is (address of) such a routine. The actual argument will be yours StoreOrtho.

On Fortran side, last argument is a routine. INTERFACE block specifies its prototype. Once passed through argument-list, it can be called as any other subroutine.

I put #include "StoreOrtho.h" to describe "whatever header file is routine StoreOrtho declared in".

However, if StoreOrtho is actually a class method (you didn't say so the first time), the things change a bit:

- In C++, the first argument to a class method is hidden this. To workaround that, you have to either a) (simpler) declare StoreOrtho as static (but note that in this case it is not instantiated per object, but only per entire class) or b) Pass this all the way round through MyDllProc arg's list back to CALL StoreOrtho from Fortran (I'm not even sure about syntax correctness of the following C++ code):

SUBROUTINE MyDllSub(...,this,StoreOrtho)
!DEC$ATTRIBUTES DLLEXPORT:: MyDllSub
...
INTEGER:: this
INTERFACE    
   SUBROUTINE StoreOrtho(this,x,y,hC,slope)   
   !DEC$ATTRIBUTES C:: StoreOrtho   
   INTEGER:: this
   REAL:: x(*),y(*),hC(*),slope(*)   
   END SUBROUTINE
END INTERFACE
...
Call StoreOrtho(this,X,Y,HEIGHT_COEF,SLOPE)
...
//====================================
//callback function declaration
typedef void (*CALLBACKPROC)(RefraDoc* pObj, 
   float* x, float* y, float* hC, float* slope);
//Fortran function declaration
extern "C" void _declspec(dllimport) __stdcall 
   MyDllSub(..., RefraDoc* pObj, CALLBACKPROC Callback);
...
MyDllSub(...,this,StoreOrtho);

IMHO it's better to avoid passing class methods around.

- Have in mind that default calling convention in VC++ is __fastcall, which is not supported in CVF. You have to use __cdecl in StoreOrtho's declaration. (!DEC$ATTRIBUTES C corresponds to __cdecl).

Jugoslav

Thanks Jugoslav for your help, but it was very confuse around here and what I have done with your info doesn't works. I don't know what I have done wrong, but ...

You will found in this reply some part of my original code, hope it will help you to understand the structure of my apps. Maybe it will help you to to make me understand what I have to do...

First in Refra.h the main class of my project I have the declaration of my fortran subroutine, who look like this:

extern "C" PROPAGATE_DLL (float* periode, double* deltaT,
const char* END_CONDITION, int L_END_CONDITION, int* flag_zoom, int* flag);

After that in RefraDoc.h I have the declaration of my StoreOrtho function:

void StoreOrtho(float x[600], float y[600], float heightCoef[600], float slope[600]);

And in RefraDoc.cpp is implementation:

extern "C" void CRefraDoc::StoreOrtho(float x[600], float y[600], float heightCoef[600], float slope[600])
{
COrthogonale *newOrtho = new COrthogonale;

newOrtho->x = x;
newOrtho->y = y;
newOrtho->heightCoef = heightCoef;
newOrtho->slope = slope;

m_aOrthoArray.Add(newOrtho);

return;
}
Where m_aOrthoArray was of type CObArray.

My dll subroutine (PROPAGATE_DLL) was called in a function of RefraDoc.cpp

This subroutine was looking like this...

SUBROUTINE PROPAGATE_DLL(PERIOD,DELTA_T,END_CONDITION,
ZOOMF,BWF)
!DEC$ ATTRIBUTES DLLEXPORT, ALIAS:'_PROPAGATE_DLL'::
PROPAGATE_DLL
...
...

! The place were I need to call my C++ function
Call StoreOrtho(THIS,X,Y,HEIGHT_COEF,SLOPE)

...
...

END

This dll subroutine was a big loop who was executed n time and on every loop I need to call my C++ function to store the result for every orthos.

Thanks for your Help. Hope you better understand My problem, and that you will help me understand how to do the call. If you have other suggestion about the way to do this your welcome.

Demo_360

My comments & additions are marked bold.

!=======================
First in Refra.h the main class of my project I have the declaration of my fortran subroutine, who look like this:

I assume you had dllimport and __stdcall here -- otherwise I don't know how it worked
extern "C" _declspec(dllimport) __stdcall PROPAGATE_DLL (float* periode, double* deltaT,
const char* END_CONDITION, int L_END_CONDITION, int* flag_zoom, int* flag, CRefraDoc* Doc, void* Callback);
I added void* to simplify things -- I'm really not sure how to typedef a class member in C++. The call should look like:

PROPAGATE_DLL (periode, deltaT, END_CONDITION, L_END_CONDITION, flag_zoom, flag, &YourRefraDoc, (void*)YourRefraDoc.StoreOrtho);

After that in RefraDoc.h I have the declaration of my StoreOrtho function:

void __cdecl StoreOrtho(float x[600], float y[600], float heightCoef[600], float slope[600]);

And in RefraDoc.cpp is implementation:

Use either __cdecl or __stdcall, but don't leave it undefined -- I think if you omit it, it defaults to __fastcall.
void __cdecl CRefraDoc::StoreOrtho(float x[600], float y[600], float heightCoef[600], float slope[600])
{
COrthogonale *newOrtho = new COrthogonale;

newOrtho->x = x;
newOrtho->y = y;
newOrtho->heightCoef = heightCoef;
newOrtho->slope = slope;

m_aOrthoArray.Add(newOrtho);

return;
}
Where m_aOrthoArray was of type CObArray.

My dll subroutine (PROPAGATE_DLL) was called in a function of RefraDoc.cpp

This subroutine was looking like this...

SUBROUTINE PROPAGATE_DLL(PERIOD,DELTA_T,END_CONDITION,
ZOOMF,BWF,this,StoreOrtho)
DON'T use ALIAS to cheat calling convention. CVF default is __stdcall and I matched that in C++ declaration. You were actualy faking __cdecl name decoration, which causes screwage of the stack
!DEC$ ATTRIBUTES DLLEXPORT:: PROPAGATE_DLL
...

INTEGER:: This
INTERFACE
SUBROUTINE StoreOrtho(THIS,X,Y,HEIGHT_COEF,SLOPE)
!DEC$ATTRIBUTES C:: StoreOrtho
INTEGER:: This
REAL:: X(*),Y(*),HEIGHT_COEF(*),SLOPE(*)
END SUBROUTINE
END INTERFACE

! The place were I need to call my C++ function
Call StoreOrtho(THIS,X,Y,HEIGHT_COEF,SLOPE)

...
...

END

I have this error message when I compile My project...

C:CogeniProjectRefraRefraDoc.cpp(406) : error C2664: 'PROPAGATE_DLL' : cannot convert parameter 8 from 'void (float [],float [],float [],float [])' to 'void (__cdecl *)(float *,float *,float *,float *)'

typedef was like this...
typedef void (*CALLBACKPROC)(float* x, float* y,float* hc, float* slope);

And StoreOrtho was declare like this:

void __cdecl StoreOrtho(float x[600], float y[600], float heightCoef[600], float slope[600]);

and implemented like that:
void CRefraDoc::StoreOrtho(float x[600], float y[600], float heightCoef[600], float slope[600])
{
COrthogonale *newOrtho = new COrthogonale;

newOrtho->x = x;
newOrtho->y = y;
newOrtho->heightCoef = heightCoef;
newOrtho->slope = slope;

m_aOrthoArray.Add(newOrtho);

return;
}

Do you have an idea Why??? Cause I don't know.

Thanks

Teh fortran subroutine was declare like this:

extern "C" _declspec(dllimport) PROPAGATE_DLL (float* periode, double* deltaT,
const char* END_CONDITION, int L_END_CONDITION, int* flag_zoom, int* flag, CRefraDoc* Doc, CALLBACKPROC Callback);

Yeah, C++ is rather unforgivable about type conversions, especially pointer-to-function casts (and I admit I'm not versatile enough in C++ either to always get it right without few tries). Basically, it complains that the actual argument (StoreOrtho) passed to Fortran routine does not match CALLBACKPROC prototype. Try either:

typedef void (__cdecl *CALLBACKPROC)(float* x, 
float* y,float* hc, float* slope);

or

typedef void (__cdecl *CALLBACKPROC)(CRefraDoc* cDoc, float* x, 
float* y,float* hc, float* slope)

(perhaps * must be replaced with []).

Also, you forgot __stdcall in PROPAGATE_DLL declaration.

Jugoslav

Hello again,

My problem was partly solve but I style have problem with the type conversion. Now my error message look like this:

C:CogeniProjectRefraRefraDoc.cpp(409) : error C2664: 'PROPAGATE_DLL' : cannot convert parameter 8 from 'void (float [],float [],float [],float [])' to 'void (__cdecl *)(float [],float [],float [],float [])'

Do you have an other try for me cause I stuck.

Thanks

Do you have __cdecl specified for StoreOrtho in RefraDoc.h? Sounds as if you don't.

Don't get me wrong, but you should have a word or two with a C++ expert, which I'm definitely not. Most of my C++ programming consists of trial-and-error method :-).

Yes I have __cdecl for StoreOrtho.

Thanks a lot for your help, I really appreciate.

I will ask to somebody else.

demo_360

OK, finally...
You can't pass a non-static class member function to a non-C++ routine. What you can do is to pass a static class member (or non-member) and let that function act as a "bridge" or "driver", i.e. which just calls non-static StoreOrtho itself.

Attached is a simple mixed fortran-C++ workspace which exhibits the technique. I hope you will be able to adapt it to your needs.

Jugoslav

I found this thread in an attempt to solve my own, related problems to calling back to several C++ functions from a FORTRAN dll. My FORTRAN dll needs to callback several C++ functions. I'm wondering if I can pass all the C++ function pointers to FORTRAN at once and save them in global variables, so that I can call the C++ routines later as needed. That would be much easier than trying to pass the function pointers in every time, and then pass them around to other internal FORTRAN subroutines that actually need to do the callback. I don't know jack about FORTRAN, but I've figured out that I can place all the INTERFACE blocks in a module (I think) and use the USE statement inside the differenent subroutines. Now if I just knew how to save the function pointers to global variables.

Michael

See here and maybe here. Beware that it's nonstandard and that it will work only under CVF.

Jugoslav

Leave a Comment

Please sign in to add a comment. Not a member? Join today