For Programmers: Free Programming Magazines  


Home > Archive > Fortran > January 2006 > Fortran DLL-Interface to Delphi









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author Fortran DLL-Interface to Delphi
Redelbach

2006-01-25, 7:10 pm

Hello,
I'm working on the Fortran side of a DLL-Interface based on Delphi.
More speciifcally, the Fortran programme loads a Delphi-DLL and has to
exchange some values with a Delphi-procedure. We exchange INTEGER,
CHARACTER and REAL values and we succeeded for
INTEGEGER values as well as CHARACTERs.
In the Fortran world, the respective variables are included in one
COMMON block and are simply passed to the Delphi-Pocedure which is
declared as an interface subroutine.
On the Delphi side, the addresses of the variables are used as a
reference for Delphi-pointers.
This technique works well for INTs and CHARs, however we have not yet
found a way to pass a REAL value in such a way that Delphi gets its
correct address. It seems weird and one might guess that there is a
basic difference between REALs and INTs when it comes to address
assignment in the mixed-language context.
Has anyone of You some kind of idea?
-Redelbach

Michael Wild

2006-01-25, 7:10 pm

Redelbach wrote:
> Hello,
> I'm working on the Fortran side of a DLL-Interface based on Delphi.
> More speciifcally, the Fortran programme loads a Delphi-DLL and has to
> exchange some values with a Delphi-procedure. We exchange INTEGER,
> CHARACTER and REAL values and we succeeded for
> INTEGEGER values as well as CHARACTERs.
> In the Fortran world, the respective variables are included in one
> COMMON block and are simply passed to the Delphi-Pocedure which is
> declared as an interface subroutine.
> On the Delphi side, the addresses of the variables are used as a
> reference for Delphi-pointers.
> This technique works well for INTs and CHARs, however we have not yet
> found a way to pass a REAL value in such a way that Delphi gets its
> correct address. It seems weird and one might guess that there is a
> basic difference between REALs and INTs when it comes to address
> assignment in the mixed-language context.
> Has anyone of You some kind of idea?
> -Redelbach
>


Are the REALS scalars or arrays? If the second, are they POINTERS or
ALLOCATABLE or have assumed shape? If any of that is the case, chances
are that your fortran compiler uses an array-descriptor and the pointer
you get in delphi is just a pointer to the descriptor, not the actual
data. The descriptor itself holds a pointer to the data, but its format
is vendor specific.

- Michael
John Mansell

2006-01-26, 3:59 am

This is all very compiler-dependent and you don't say which compiler you
are using, but you seem to have solved the basics, so...

In the Delphi part, the REALs from the Fortran should be declared as
Single (and var). Fortran DOUBLE PRECISION corresponds (obviously) with
Delphi Double. The Delphi Real (default real) is Double (according to
the Object Pascal Help file).

In Delphi there is also Real48, a 6-byte thing, which used to be the
default Real. Avoid.

If you are using a f95 compiler, you should also be able to pass Delphi
records to the Fortran as Fortran derived types with the SEQUENCE
attribute.

In message <1138211300.555833.281100@g47g2000cwa.googlegroups.com>,
Redelbach <A.Redelbach@gsi.de> writes
>Hello,
>I'm working on the Fortran side of a DLL-Interface based on Delphi.
>More speciifcally, the Fortran programme loads a Delphi-DLL and has to
>exchange some values with a Delphi-procedure. We exchange INTEGER,
>CHARACTER and REAL values and we succeeded for
>INTEGEGER values as well as CHARACTERs.
>In the Fortran world, the respective variables are included in one
>COMMON block and are simply passed to the Delphi-Pocedure which is
>declared as an interface subroutine.
>On the Delphi side, the addresses of the variables are used as a
>reference for Delphi-pointers.
>This technique works well for INTs and CHARs, however we have not yet
>found a way to pass a REAL value in such a way that Delphi gets its
>correct address. It seems weird and one might guess that there is a
>basic difference between REALs and INTs when it comes to address
>assignment in the mixed-language context.
>Has anyone of You some kind of idea?
>-Redelbach
>


--
John Mansell john at wcompsys dot co dot uk

Catherine Rees Lay

2006-01-26, 3:59 am

If I remember correctly (and it's a while since I did this) there is an
issue with real-valued functions. Even if the calling conventions etc.
are the same, the mechanism for these isn't specified by the calling
convention and Delphi does it differently. I found it sufficiently
confusing that I've always used subroutines.

If you're just passing them as variables, John's answer has everything
you need. But make absolutely sure you have the same calling convention
on each side. Getting it wrong doesn't give error messages, and often
very very nearly works :(

Catherine.


John Mansell wrote:
> This is all very compiler-dependent and you don't say which compiler you
> are using, but you seem to have solved the basics, so...
>
> In the Delphi part, the REALs from the Fortran should be declared as
> Single (and var). Fortran DOUBLE PRECISION corresponds (obviously) with
> Delphi Double. The Delphi Real (default real) is Double (according to
> the Object Pascal Help file).
>
> In Delphi there is also Real48, a 6-byte thing, which used to be the
> default Real. Avoid.
>
> If you are using a f95 compiler, you should also be able to pass Delphi
> records to the Fortran as Fortran derived types with the SEQUENCE
> attribute.
>
> In message <1138211300.555833.281100@g47g2000cwa.googlegroups.com>,
> Redelbach <A.Redelbach@gsi.de> writes
>

Redelbach

2006-01-30, 3:57 am

Thanks for Your comments. Well, I make use of the Compaq Visual Fortran
6.1 Compiler and I only pass scalar REAL*8 values to the Delphi
Procedure. As mentioned, REAL*8 and INTEGER*4 values are handled in a
analogous way at least on the Fortran side of the DLL.
However the address value in Delphi is not correct for the REAL-values
as opposed to the INTs.
Do You see any further possibility?
Andreas

Redelbach

2006-01-30, 3:57 am

Thank You for the comments.
Well, I am using the Compaq Visual Fortran Compiler 6.1 and for Delphi
the Borland 7 Version is in use.
As for the data types: We use only scalar variables and the REAL
variable is a REAL*8 value in Fortran corresponding to a double value
in Delphi.
Do You see any mismatch at this point?
Andreas Redelbach

Redelbach

2006-01-30, 3:57 am

The calling of the Delphi procedure is done in a subroutine of an
interface block. So we are not using explcit functions, but we see a
difference in the address values that Delphi gets for
INTEGER*4(correct) and REAL*8(wrong).
Do You think this is a matter of the Compaq Visual Fortran 6.1 compiler
in connection with Borland 7 Delphi?

Andreas Redelbach

Jugoslav Dujic

2006-01-30, 3:57 am

Redelbach wrote:
| The calling of the Delphi procedure is done in a subroutine of an
| interface block. So we are not using explcit functions, but we see a
| difference in the address values that Delphi gets for
| INTEGER*4(correct) and REAL*8(wrong).
| Do You think this is a matter of the Compaq Visual Fortran 6.1 compiler
| in connection with Borland 7 Delphi?

I think you should show us some real code instead of describing it;
in particular:

* Fortran INTERFACE block to Delphi function
* Fortran call to the function, including declarations of all relevant
variables
* Delphi prototype of the function.

--
Jugoslav
___________
www.xeffort.com

Please reply to the newsgroup.
You can find my real e-mail on my home page above.
Redelbach

2006-01-30, 8:00 am

Jugoslav Dujic schrieb:

> Redelbach wrote:
> | The calling of the Delphi procedure is done in a subroutine of an
> | interface block. So we are not using explcit functions, but we see a
> | difference in the address values that Delphi gets for
> | INTEGER*4(correct) and REAL*8(wrong).
> | Do You think this is a matter of the Compaq Visual Fortran 6.1 compiler
> | in connection with Borland 7 Delphi?
>
> I think you should show us some real code instead of describing it;
> in particular:
>
> * Fortran INTERFACE block to Delphi function
> * Fortran call to the function, including declarations of all relevant
> variables
> * Delphi prototype of the function.
>
> --
> Jugoslav
> ___________
> www.xeffort.com
>
> Please reply to the newsgroup.
> You can find my real e-mail on my hom e page above.




So, here are the relevant parts of the code under investigation:

! COMMON-Block
COMMON /INTERFACE/ pInterfaceInst, piInstanceId, piDone,
piOptions, pcValueName, pfValue
INTEGER*4 piInstanceId
INTEGER*4 pInterfaceInst
INTEGER*4 piDone, piOptions
CHARACTER*8 pcValueName
REAL*8 pfValue

! INTERFACE-Block to Delphi
interface
subroutine GetFloatValue(liInstanceId, lccValueName, lcfValue
, lciOptions,
lciDone)
Integer*4 liInstanceId,lciDone,lciOptions
REAL*8 lcfValue
CHARACTER*8 lccValueName
end subroutine GetFloatValue
end interface

pointer (pGetFloatValue,GetFloatValue)

pGetFloatValue = getprocaddress(pInterfaceInst, "GetFloatValue"C)

! Test-Values
pcValueName = 'kL_L1QS1'C
piOptions = 0
pfvalue = 10.2D0

CALL GetFloatValue(piInstanceId, pcValueName, pfValue, piOptions,
piDone)

! Delphi Procedure Prototype
Procedure GetFloatValue (piInstanceId: PInteger;
pcValueName: PChar;
pfValue: PDouble;
piOptions: PInteger;
piDone: PInteger);

As mentioned, the method works well for the INTEGER*4 values and their
addresses, but not for REAL*8 pfValue...
Any useful comments are appreciated,
Andreas Redelbach

Jugoslav Dujic

2006-01-30, 8:00 am

Redelbach wrote:
| Jugoslav Dujic schrieb:
|
|| Redelbach wrote:
||| The calling of the Delphi procedure is done in a subroutine of an
||| interface block. So we are not using explcit functions, but we see a
||| difference in the address values that Delphi gets for
||| INTEGER*4(correct) and REAL*8(wrong).
||| Do You think this is a matter of the Compaq Visual Fortran 6.1 compiler
||| in connection with Borland 7 Delphi?
||
|| I think you should show us some real code instead of describing it;
|| in particular:
||
|| * Fortran INTERFACE block to Delphi function
|| * Fortran call to the function, including declarations of all relevant
|| variables
|| * Delphi prototype of the function.

Comments inline:

| So, here are the relevant parts of the code under investigation:
|
| ! COMMON-Block
| COMMON /INTERFACE/ pInterfaceInst, piInstanceId, piDone,
| piOptions, pcValueName, pfValue
| INTEGER*4 piInstanceId
| INTEGER*4 pInterfaceInst
| INTEGER*4 piDone, piOptions
| CHARACTER*8 pcValueName
| REAL*8 pfValue
|
| ! INTERFACE-Block to Delphi
| interface
| subroutine GetFloatValue(liInstanceId, lccValueName, lcfValue
| , lciOptions,
| lciDone)
| Integer*4 liInstanceId,lciDone,lciOptions
| REAL*8 lcfValue
| CHARACTER*8 lccValueName
--------------------------------^
Here lies the dragon #1. In Fortran, character length must be passed
along with the string address, so that the called routine can
know the length. Specifying (*8) is not good enough.

By default, CVF supplies the length immediately after string
argument (as a hidden one). Delphi doesn't except the length
and "reads" the next argument (lcfValue) instead.

To suppress string length passing, add to INTERFACE block:

!DEC$ATTRIBUTES REFERENCE:: lccValueName
CHARACTER(*) lccValueName

| end subroutine GetFloatValue
| end interface
|
| pointer (pGetFloatValue,GetFloatValue)
|
| pGetFloatValue = getprocaddress(pInterfaceInst, "GetFloatValue"C)
|
| ! Test-Values
| pcValueName = 'kL_L1QS1'C
| piOptions = 0
| pfvalue = 10.2D0
|
| CALL GetFloatValue(piInstanceId, pcValueName, pfValue, piOptions,
| piDone)
|
| ! Delphi Procedure Prototype
| Procedure GetFloatValue (piInstanceId: PInteger;
| pcValueName: PChar;
| pfValue: PDouble;
| piOptions: PInteger;
| piDone: PInteger);

I'd rephrase this as:

Procedure GetFloatValue (var iInstanceId: Integer;
pcValueName: PChar;
var fValue: Double;
var iOptions: Integer;
var iDone: Integer); stdcall;

I don't know what's the default Delphi 7 calling convention, but
explicitly specifying stdcall won't hurt (note: stdcall is CVF
default, but not IVF default, if you ever want to port this
to Intel Visual Fortran). Specifying "var" instead of pointers would
reduce one level of indirection. This is not a problem though -- your
problem lied in the string argument.

--
Jugoslav
___________
www.xeffort.com

Please reply to the newsgroup.
You can find my real e-mail on my home page above.
Michael Wild

2006-01-30, 8:00 am

Redelbach wrote:
> Thanks for Your comments. Well, I make use of the Compaq Visual Fortran
> 6.1 Compiler and I only pass scalar REAL*8 values to the Delphi
> Procedure. As mentioned, REAL*8 and INTEGER*4 values are handled in a
> analogous way at least on the Fortran side of the DLL.
> However the address value in Delphi is not correct for the REAL-values
> as opposed to the INTs.
> Do You see any further possibility?
> Andreas
>


hmmm, i did these things once using intel fc 9 and c++ and had no
problems using reals. have you considered writing just a couple of small
test-programs to track the thing down? maybe even in c (as it is more
primitive than delphi and doesn't feature things that might get into
your way).
Redelbach

2006-01-31, 4:08 am

Hi Jugoslav,

You really helped me a lot!
As You expected, the problem was the 'hidden' passing of the CHARACTER
Length.
Now I changed the relevant parts of my code:

! In Function declarations
CHARACTER*12 pcValueName
! In Interface Subroutine Declarations
!DEC$ATTRIBUTES REFERENCE:: lccValueName
CHARACTER(*) lccValueName
! Set Variable
pcValueName = 'kL_L1QS1'

! Call Function
CALL GetFloatValue(piInstanceId, pcValueName, pfValue, piOptions,
piDone)

In addition, I specified
/iface:nomixed_str_len_arg
in the Project Options for Code Generation

Now I obtain the right results from the Delphi DLL,
Andreas


Jugoslav Dujic schrieb:

> Redelbach wrote:
> | Jugoslav Dujic schrieb:
> |
> || Redelbach wrote:
> ||| The calling of the Delphi procedure is done in a subroutine of an
> ||| interface block. So we are not using explcit functions, but we see a
> ||| difference in the address values that Delphi gets for
> ||| INTEGER*4(correct) and REAL*8(wrong).
> ||| Do You think this is a matter of the Compaq Visual Fortran 6.1 compiler
> ||| in connection with Borland 7 Delphi?
> ||
> || I think you should show us some real code instead of describing it;
> || in particular:
> ||
> || * Fortran INTERFACE block to Delphi function
> || * Fortran call to the function, including declarations of all relevant
> || variables
> || * Delphi prototype of the function.
>
> Comments inline:
>
> | So, here are the relevant parts of the code under investigation:
> |
> | ! COMMON-Block
> | COMMON /INTERFACE/ pInterfaceInst, piInstanceId, piDone,
> | piOptions, pcValueName, pfValue
> | INTEGER*4 piInstanceId
> | INTEGER*4 pInterfaceInst
> | INTEGER*4 piDone, piOptions
> | CHARACTER*8 pcValueName
> | REAL*8 pfValue
> |
> | ! INTERFACE-Block to Delphi
> | interface
> | subroutine GetFloatValue(liInstanceId, lccValueName, lcfValue
> | , lciOptions,
> | lciDone)
> | Integer*4 liInstanceId,lciDone,lciOptions
> | REAL*8 lcfValue
> | CHARACTER*8 lccValueName
> --------------------------------^
> Here lies the dragon #1. In Fortran, character length must be passed
> along with the string address, so that the called routine can
> know the length. Specifying (*8) is not good enough.
>
> By default, CVF supplies the length immediately after string
> argument (as a hidden one). Delphi doesn't except the length
> and "reads" the next argument (lcfValue) instead.
>
> To suppress string length passing, add to INTERFACE block:
>
> !DEC$ATTRIBUTES REFERENCE:: lccValueName
> CHARACTER(*) lccValueName
>
> | end subroutine GetFloatValue
> | end interface
> |
> | pointer (pGetFloatValue,GetFloatValue)
> |
> | pGetFloatValue = getprocaddress(pInterfaceInst, "GetFloatValue"C)
> |
> | ! Test-Values
> | pcValueName = 'kL_L1QS1'C
> | piOptions = 0
> | pfvalue = 10.2D0
> |
> | CALL GetFloatValue(piInstanceId, pcValueName, pfValue, piOptions,
> | piDone)
> |
> | ! Delphi Procedure Prototype
> | Procedure GetFloatValue (piInstanceId: PInteger;
> | pcValueName: PChar;
> | pfValue: PDouble;
> | piOptions: PInteger;
> | piDone: PInteger);
>
> I'd rephrase this as:
>
> Procedure GetFloatValue (var iInstanceId: Integer;
> pcValueName: PChar;
> var fValue: Double;
> var iOptions: Integer;
> var iDone: Integer); stdcall;
>
> I don't know what's the default Delphi 7 calling convention, but
> explicitly specifying stdcall won't hurt (note: stdcall is CVF
> default, but not IVF default, if you ever want to port this
> to Intel Visual Fortran). Specifying "var" instead of pointers would
> reduce one level of indirection. This is not a problem though -- your
> problem lied in the string argument.
>
> --
> Jugoslav
> ___________
> www.xeffort.com
>
> Please reply to the newsgroup.
> You can find my real e-mail on my home page above.


Jugoslav Dujic

2006-01-31, 7:58 am

Redelbach wrote:
| Hi Jugoslav,
|
| In addition, I specified
| /iface:nomixed_str_len_arg
| in the Project Options for Code Generation

That won't help in this case (but won't do harm, either). Not really
relevant, since the problem is solved, but let me comment a bit for
someone's future reference:

CVF has somewhat peculiar (but perfectly fine as far as Fortran
standard is concerned) combination of STDCALL calling convention and
string-length-immediately-after-string argument. Most other compilers
(including successor IVF) have C (cdecl) calling convention and
string-length-after-all-other-arguments.

With cdecl convention, the calling routine cleans the stack. That
means that, if the called routine expects e.g. 5 arguments, you
may call it with 12, and everything will work -- the excess will
be ignored and stack pointer will be correctly reverted after
the call. Also, if you call it with 4 arguments, and the latest
is not used by the called routine, you will also get by. If
/iface:nomixed_str_len_arg is in effect and the called routine
is not in Fortran, the latest string length argument will simply be
ignored. Many mixed-language projects (ab)use this feature to
pass string arguments.

However, with stdcall, the called routine cleans the stack, and
the number of real arguments must match. Thus, even if
/iface:nomixed_str_len_arg is in effect, AND you supplied the
hidden string length in excess, the stack will be off by 4 bytes
at the end and various Bad Things will happen. !DEC$ATTRIBUTES
REFERENCE suppresses string length sending.

Normally, compiler decorates stdcall routines with @n appended,
where n is number of bytes for arguments on the stack, so that
mismatch cannot happen. (So CVF is more picky, if far from perfect,
about mismatches in argument lists). However, in your case, the
decoration is stripped off by Delphi compiler during dllexport so
you have "GetFloatValue" instead of "_GetFloatValue@20".

--
Jugoslav
___________
www.xeffort.com

Please reply to the newsgroup.
You can find my real e-mail on my home page above.
Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2009 codecomments.com