forums.silverfrost.com Forum Index forums.silverfrost.com
Welcome to the Silverfrost forums
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Passing INTEGER*2 arguments using STDCALL convention fails
Goto page 1, 2  Next
 
Post new topic   Reply to topic    forums.silverfrost.com Forum Index -> Support
View previous topic :: View next topic  
Author Message
qt



Joined: 23 Aug 2005
Posts: 46
Location: Berlin, Germany

PostPosted: Sun Jun 12, 2011 9:20 pm    Post subject: Passing INTEGER*2 arguments using STDCALL convention fails Reply with quote

Hello!

I tried to use the WinAPI/ODBC function SQLBindCol whose second argument requires a short unsigned integer to be passed. And since I didn't get this to work with FTN95 for quite a while, I investigated somewhat more and finally came away with the opinion that something with passing 1-byte and 2-bytes integers is not properly working when using STDCALL convention. I have set up an example program which calls a .dll containing two subroutines which conform to STDCALL convention. Here is one of these subroutines.
Code:

SUBROUTINE PassIntegers( i1Arg, i2Arg, i4Arg )
  !DEC$ ATTRIBUTES STDCALL, DLLEXPORT, ALIAS:'PassIntegers' :: PassIntegers
  ! STDCALL causes all parameters to be passed by value.
  INTEGER*1, INTENT(IN) :: i1Arg
  INTEGER*2, INTENT(IN) :: i2Arg
  INTEGER*4, INTENT(IN) :: i4Arg

  PRINT*, '   PassIntegers: Arguments'
  PRINT*, '      1-Byte Integer i1Arg = ', i1Arg
  PRINT*, '      2-Byte Integer i2Arg = ', i2Arg
  PRINT*, '      4-Byte Integer i4Arg = ', i4Arg

END SUBROUTINE PassIntegers

I used Compaq Visual Fortran to create the .DLL (the metacommand !DEC$ ATTRIBUTES STDCALL, ... causes that the routine has to be called using STDCALL convention which means all arguments are to be passed by value).
I call this routine in a FTN95 program (v5.50 and v6.10 tested). Here is an excerpt from this:

Code:

   USE PassIntModule   ! defines BYTE, SHORT, LONG
   ! and also contains
   !   STDCALL PassIntegers 'PassIntegers' ( val, val, val )
   INTEGER (BYTE) :: i1Int       ! INTEGER*1
   INTEGER (SHORT) :: i2Int     ! INTEGER*2
   INTEGER (LONG) i4Int          ! INTEGER*4
   SAVE

   i2Int = 3
...
   PRINT*, 'CALL PassIntegers( 1_1, 2_2, 3 )'
   CALL PassIntegers( 1_1, 2_2, 3 )   ! -> display: 1, 2, 3 (OK)

   i1Int = 7
   i2Int = 45
   i4Int = 19
   CALL PassIntegers( i1Int, i2Int, i4Int )   ! -> display: 7, 3 (!!! should be 45), 19  (NOT OK)


For some unknown reason, the short integer argument (no. 2) is not passed correctly. Its value before the call is obviously 45, but the routine says its value is 3!

I am ready to send the complete source codes, PLATO and CVF projects for testing. Please let me know where to send this to.

Regards,

Jörg Kuthe
QT software
Back to top
View user's profile Send private message Visit poster's website
davidb



Joined: 17 Jul 2009
Posts: 560
Location: UK

PostPosted: Mon Jun 13, 2011 12:48 am    Post subject: Reply with quote

Can we see how you define BYTE, SHORT and LONG, since its kind of important? I can see what you have done in the call that works since you use literal constants (e.g. 2_2) but not in the second example. You need

integer, parameter :: BYTE=1, SHORT=2, LONG=3

If you have LONG=4 you will have a problem.

I am not familiar with CVF but I have created a DDL that uses a short in C++ and have successfully called it from FTN95. So if there is an error, it is possibly in the CVF DLL.

My "Microsoft Visual C++" (I used the Free Express edition) and FTN95 (version 6.1) code is below. This code just takes a short integer by value, and passes it back by reference so it can be printed by FTN95.

C++ (snippet)

Code:

extern "C" {
__declspec(dllexport) void _stdcall passinteger(short i, short *j);
}

void _stdcall passinteger(short i, short *j)
{
   *j = i;
}


FTN95 (Silverfrost) code

Code:

program anon

    STDCALL passinteger 'passinteger' (val, ref)
    integer*2 :: i, j

    i = 45

    call passinteger(i,j)

    print *,j  !<< prints 45

end program
Back to top
View user's profile Send private message
qt



Joined: 23 Aug 2005
Posts: 46
Location: Berlin, Germany

PostPosted: Mon Jun 13, 2011 8:22 am    Post subject: Reply with quote

Dear David,

thank you for your prompt reply.

Here is the definition of these KINDs:
Code:

   INTEGER , PARAMETER :: BYTE = SELECTED_INT_KIND(2)       ! 10**2, for 1-byte integers (8-bit, signed)
   INTEGER , PARAMETER :: SHORT = SELECTED_INT_KIND(4)       ! 10**4, for short integers (16-bit, signed)
   INTEGER , PARAMETER :: LONG = SELECTED_INT_KIND(8)       ! 10**8, for long integers (32-bit, signed)

That should work for any Fortran 9x compiler.

Please extend your FTN95 program by another call, e.g.
Code:

    i = -3
    call passinteger(i,j)

and check, if j == -3. My test program does the same. At the first call the argument's value is passed correctly. When changing the argument's value and calling the routine a second time it fails.
Again, I am happy to send all project files, if you let me know where to send it to.
Thank you.

Regards,

Jörg Kuthe
QT software[/code]
Back to top
View user's profile Send private message Visit poster's website
davidb



Joined: 17 Jul 2009
Posts: 560
Location: UK

PostPosted: Mon Jun 13, 2011 9:25 am    Post subject: Reply with quote

Hi Qt

I will post this now. I added another call to my C++ generated DLL and it did not work, giving a similar result to yours, i.e. it appears that the short is not passed properly the second time. Here is the code:

Code:

program anon

    STDCALL passinteger 'passinteger' (val, ref)
    integer*2 :: i, j, k
   
    i = 45

    call passinteger(i,j)

    print *,j  !<< prints 45

    i = -3
   
    call passinteger(i,k)

    print *,k  !<< prints 45 ?? _BUT_ should print -3
end program


I found a work-around, which is to to put i in brackets to force a pass by value but I don't think this is necessary and there is still a bug. See code below. Hopefully you can use this workaround in your original application.

Code:

program anon

    STDCALL passinteger 'passinteger' (val, ref)
    integer*2 :: i, j, k
   
    i = 45

    call passinteger(i,j)

    print *,j  !<< prints 45

    i = -3
   
    call passinteger((i),k)

    print *,k  !<< prints -3
end program



I wrote some code to build my DLL with FTN95. This was supposed to let Paul have a look at your problem without needing to worry about using CVF or C++ to build the DLL part. However, I found that if the DLL is built with FTN95 it works correctly!!!

So clearly this BUG will need to be fixed with a DLL built using C++. And any "fix" will need to ensure that DLLs created in FTN95 still work.

Here is my code to build the test DLL in FTN95.
Code:

! Let SLINK Create the default LibMain
! Set linker options to export all

! Subroutine exported by the DLL
F_STDCALL subroutine passinteger(i, j)
   integer*2, intent (in) :: i
   integer*2, intent (out) :: j
   j = i
   return
end subroutine passinteger


Let's hope they can look at this pretty quickly, at least to confirm the issue. Very Happy


Last edited by davidb on Mon Jun 13, 2011 10:46 am; edited 1 time in total
Back to top
View user's profile Send private message
qt



Joined: 23 Aug 2005
Posts: 46
Location: Berlin, Germany

PostPosted: Mon Jun 13, 2011 10:45 am    Post subject: Reply with quote

Hi David,

thanks a lot. Again, if Paul would like to have my Plato and CVF project files, I am happy to send them.

All the best,

Jörg
Back to top
View user's profile Send private message Visit poster's website
davidb



Joined: 17 Jul 2009
Posts: 560
Location: UK

PostPosted: Mon Jun 13, 2011 10:48 am    Post subject: Reply with quote

Thanks qt, I made a few edits to my post as you were replying.
Back to top
View user's profile Send private message
PaulLaidler
Site Admin


Joined: 21 Feb 2005
Posts: 7928
Location: Salford, UK

PostPosted: Mon Jun 13, 2011 11:25 am    Post subject: Reply with quote

I have had a quick look at this but I am not clear at this stage about the what is happening.

Before taking this any further I would want to clarify that STDCALL relates to the way the argument stack is handled and cleaned up by the caller and the routine. For FTN95 etc you still need to use REF or VAL etc. for every argument in the interface. VAL is not the default.
Back to top
View user's profile Send private message AIM Address
davidb



Joined: 17 Jul 2009
Posts: 560
Location: UK

PostPosted: Mon Jun 13, 2011 12:13 pm    Post subject: Re: Reply with quote

PaulLaidler wrote:
I have had a quick look at this but I am not clear at this stage about the what is happening.

Welcome to the club!

PaulLaidler wrote:

Before taking this any further I would want to clarify that STDCALL relates to the way the argument stack is handled and cleaned up by the caller and the routine. For FTN95 etc you still need to use REF or VAL etc. for every argument in the interface. VAL is not the default.

Yes, understood, and I think we both use VAL, REF etc properly.
Back to top
View user's profile Send private message
qt



Joined: 23 Aug 2005
Posts: 46
Location: Berlin, Germany

PostPosted: Mon Jun 13, 2011 12:16 pm    Post subject: Reply with quote

Hi Paul,

according to CVFs dcoumentation, STDCALL usually causes:
+ all arguments to be passed by value
+ routine names to be converted and decorated
+ caller does NOT clean up the stack (so it's being done by the callee).
As you can see from my code excerpts above that arguments are explicitely passed by value.
I now have also created the DLL using Intel Visual Fortran and used this with my FTN95 test program. Same results. Then I have changed the calling convention to "C" and left remaining declarations unchanged. That causes that the caller has to clean the stack. The results have been the same: 1-byte and 2-byte integer arguments are not passed correctly.

Regards,

Jörg
QT software
Back to top
View user's profile Send private message Visit poster's website
davidb



Joined: 17 Jul 2009
Posts: 560
Location: UK

PostPosted: Mon Jun 13, 2011 12:22 pm    Post subject: Re: Reply with quote

qt wrote:
I now have also created the DLL using Intel Visual Fortran and used this with my FTN95 test program. Same results.


Same bug with CVF, Ifort and MVC++ seems convincing something is wrong.

qt wrote:

Then I have changed the calling convention to "C" and left remaining declarations unchanged. That causes that the caller has to clean the stack. The results have been the same: 1-byte and 2-byte integer arguments are not passed correctly.


Now that is interesting. I didn't try using __cdecl in my C++ code. It points to it being a more general problem, perhaps with the use of VAL in the STDCALL, C_EXTERNAL lines (as appropriate).

If I didn't know FTN95 better, I would think it was some kind of optimization problem. Razz
Back to top
View user's profile Send private message
PaulLaidler
Site Admin


Joined: 21 Feb 2005
Posts: 7928
Location: Salford, UK

PostPosted: Tue Jun 14, 2011 8:59 pm    Post subject: Reply with quote

I will investigate this as soon as I can.
Back to top
View user's profile Send private message AIM Address
PaulLaidler
Site Admin


Joined: 21 Feb 2005
Posts: 7928
Location: Salford, UK

PostPosted: Wed Jun 15, 2011 10:09 am    Post subject: Reply with quote

Just to clarify the situation, when you use FTN95 to compile a Fortran subprogram, arguments are always passed by reference. This is the Fortran standard and FTN95 does not have an extension that allows you to pass by value.

When you use F_STDCALL with an FTN95 subprogram (in a DLL in this case), arguments are still passed by reference.

The Fortran main program posted at the start of this thread will only work if PassIntegers is provided as a C function compiled with SCC or possibly with a Fortran (etc.) subroutine compiled using a third party compiler but this is not guaranteed.

I don't have access to a CVF compiler so I am not able to test the interaction of of a CVF dll with an FTN95 main program. I can test the FTN95/SCC interaction but this does not seem relevant.
Back to top
View user's profile Send private message AIM Address
davidb



Joined: 17 Jul 2009
Posts: 560
Location: UK

PostPosted: Wed Jun 15, 2011 1:22 pm    Post subject: Reply with quote

What you seem to be saying is that FTN95 code always needs to pass values by reference to DLLs (since this is the natural method in Fortran). Therefore the use of VAL in STDCALL does not work. It should be creating a temporary variable and passing a reference to that variable.

Moreover, as I posted above, I can force such a "pass-by-value" (really reference to a temporary is passed) in Fortran using the old trick of just putting the actual argument in brackets.

Look at my code (passinteger was built in MSVC++):

Code:

program anon

    STDCALL passinteger 'passinteger' (val, ref)  !<<< VAL, REF
    integer*2 :: i, j, k
   
    i = 45

    call passinteger(i,j)

    print *,j  !<< prints 45

    i = -3
   
    call passinteger((i),k)

    print *,k  !<< prints -3
end program


This is probably what VAL needs to force to happen behind the scenes.

My C++ code does expect an int to be passed by value for the first argument though. And it seems to work with the above FTN95 code.
(see post 2 above for the C++ code)


Perhaps it is creating the temporary but passing the reference to the original value.?


If it can't be done, then it might be worth expanding the entry in the Knowedebase (KBase) to provide this additional advice.

I'm not sure why the first call works and the second doesn't though in the original code in the first post. I do suspect its a problem with VAL that would need to be investigated (it clealy does something!).

Hope this is helpful.

PS. If you want my passinteger DLL for your tests, send me a PM and I can send it (source code and DLL).

David.


Last edited by davidb on Wed Jun 15, 2011 2:40 pm; edited 4 times in total
Back to top
View user's profile Send private message
brucebowler
Guest





PostPosted: Wed Jun 15, 2011 1:56 pm    Post subject: Re: Reply with quote

davidb wrote:
Moreover, as I posted above, I can force a pass-by value in Fortran using the old trick of just putting the actual argument in brackets (this is how to pass by value in fortran).


Please pardon the pun, but can you provide a reference for the above statement? If it's an old trick why do many vendors provide extensions like %val() to pass by value?
Back to top
davidb



Joined: 17 Jul 2009
Posts: 560
Location: UK

PostPosted: Wed Jun 15, 2011 2:21 pm    Post subject: Re: Reply with quote

brucebowler wrote:
davidb wrote:
Moreover, as I posted above, I can force a pass-by value in Fortran using the old trick of just putting the actual argument in brackets (this is how to pass by value in fortran).


Please pardon the pun, but can you provide a reference for the above statement? If it's an old trick why do many vendors provide extensions like %val() to pass by value?


I edited my last post to try to make it clearer. Enclosing an argument in brackets causes a temporary variable to be created which is passed by reference (usually).

Actually, however, the Fortran standard doesn't say that arguments must be passed by reference, only that it must appear like that.

Compiler vendors provide %val extensions so that a "proper" pass by value can be implemented. There is also a VALUE passing option in Fortran 2003 I believe.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    forums.silverfrost.com Forum Index -> Support All times are GMT + 1 Hour
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group