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

Do I need different interface for VB and VBA when calling Fortran DLL?

DavidWhite
Valued Contributor II
1,150 Views
One of my customers is trying to call my Fortan DLL from VB5. I orginally set this up for access from Excel VBA. My customer is having problems because he cannot access a debug version of my DLL. I have tried replicating the scenario on my own machine, and am getting a memory violation on the call to the DLL.

Is there a difference between VB and VBA in how I should declare the DLL entry point? In VB,I have this delcaration:

Private Declare Sub BayerDensity_F Lib "AHEAProps.dll" (TempC As Double, TAgpL As Double, _
TCgpL As Double, Al2O3gpL As Double, TOCgpL As Double, Na2SO4gpL As Double, _
NaClgpL As Double, Value As Double, ByVal Units As String)

with the corresponding export statement in Fortran

!DEC$ ATTRIBUTES DLLEXPORT, STDCALL, ALIAS:'BayerDensity_F' :: BayerDensity_F
!DEC$ ATTRIBUTES REFERENCE :: TempC, TAgpL, TCgpL, Al2O3gpL, TOCgpL, Na2SO4gpL, NaClgpL, Value, Units

Value and Units are returned from the DLL, all the other arguments are inputs.


Thanks,

David
0 Kudos
17 Replies
Jugoslav_Dujic
Valued Contributor II
1,150 Views
Quoting - David White
One of my customers is trying to call my Fortan DLL from VB5. I orginally set this up for access from Excel VBA. My customer is having problems because he cannot access a debug version of my DLL. I have tried replicating the scenario on my own machine, and am getting a memory violation on the call to the DLL.

Is there a difference between VB and VBA in how I should declare the DLL entry point? In VB,I have this delcaration:

Private Declare Sub BayerDensity_F Lib "AHEAProps.dll" (TempC As Double, TAgpL As Double, _
TCgpL As Double, Al2O3gpL As Double, TOCgpL As Double, Na2SO4gpL As Double, _
NaClgpL As Double, Value As Double, ByVal Units As String)

with the corresponding export statement in Fortran

!DEC$ ATTRIBUTES DLLEXPORT, STDCALL, ALIAS:'BayerDensity_F' :: BayerDensity_F
!DEC$ ATTRIBUTES REFERENCE :: TempC, TAgpL, TCgpL, Al2O3gpL, TOCgpL, Na2SO4gpL, NaClgpL, Value, Units

Value and Units are returned from the DLL, all the other arguments are inputs.


Thanks,

David

  1. Older VB versions pass arguments ByRef by default, while newer ones (starting from VB.NET) pass them ByVal. That shouldn't affect you, but I'd rather spell ByRef in VB code than second-guess the intent.
  2. How is Units argument declared in Fortran? String arguments can be tricky.
  3. Why don't you debug your dll and see where exactly you get access violation problems?
Debug vs. release should not make a difference; however, in the Debug version there are numerous checks turned on, so what raises an error in Debug might just make a silent memory corruption in Release. However, there is too little data to work on.
0 Kudos
DavidWhite
Valued Contributor II
1,150 Views
Quoting - Jugoslav Dujic

  1. Older VB versions pass arguments ByRef by default, while newer ones (starting from VB.NET) pass them ByVal. That shouldn't affect you, but I'd rather spell ByRef in VB code than second-guess the intent.
  2. How is Units argument declared in Fortran? String arguments can be tricky.
  3. Why don't you debug your dll and see where exactly you get access violation problems?
Debug vs. release should not make a difference; however, in the Debug version there are numerous checks turned on, so what raises an error in Debug might just make a silent memory corruption in Release. However, there is too little data to work on.

The string argument is padded with 20 spaces before the call, and defined as CHAR(LEN=20) in Fortran.

The DLL works perfecting from VBA, but is throwing GP faults when called by VB. Appears that as soon as Value or Units have their value changed.

Thanks for any ideas.

Should add -I have now set up a VB / Fortran project on my machine, and the error happens in Debug mode here. I can step into my fortran routines, and they fail as noted above as soon as the argument variables are changed.

D
0 Kudos
ArturGuzik
Valued Contributor I
1,150 Views
Quoting - David White

The string argument is padded with 20 spaces before the call, and defined as CHAR(LEN=20) in Fortran.

The DLL works perfecting from VBA, but is throwing GP faults when called by VB. Appears that as soon as Value or Units have their value changed.

Thanks for any ideas.

Should add -I have now set up a VB / Fortran project on my machine, and the error happens in Debug mode here. I can step into my fortran routines, and they fail as noted above as soon as the argument variables are changed.

D
David,

(1) is this VB.NET or VB 6.0?

(2) do you have any INTENT (IN)/(OUT)/(INOUT) on Fortran side? I had once issues with that one, as ByVal, ByRef and INTENT combinations can't simetimes go together.

A.
0 Kudos
DavidWhite
Valued Contributor II
1,150 Views
Quoting - ArturGuzik
David,

(1) is this VB.NET or VB 6.0?

(2) do you have any INTENT (IN)/(OUT)/(INOUT) on Fortran side? I had once issues with that one, as ByVal, ByRef and INTENT combinations can't simetimes go together.

A.

I'm using VB 2005 which is part of VS2005. I set it up as a console application - I assume this is VB, but could be wrong.

I have discoverd that ByRef works with INTENT(OUT), but ByVal won't, but I need to use ByVal with String variables.

D
0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,150 Views
Quoting - David White

I'm using VB 2005 which is part of VS2005. I set it up as a console application - I assume this is VB, but could be wrong.

I have discoverd that ByRef works with INTENT(OUT), but ByVal won't, but I need to use ByVal with String variables.

D

INTENT does not affect the calling sequence (or the generated code, unless perhaps some optimizations) whatsoever. It chiefly serves self-documenting/error-preventing purposes, much like IMPLICIT NONE. So it's best left out when discussing mixed-language issues.

Yes, Fortran string arguments should be declared ByVal; that's fairly odd VB convention. As I recall, ByRef roughly corresponds to Fortran CHARACTER(), POINTER, i.e. the called routine may reallocate storage for it. Did that alone solve the problem?
0 Kudos
DavidWhite
Valued Contributor II
1,150 Views
Quoting - Jugoslav Dujic

INTENT does not affect the calling sequence (or the generated code, unless perhaps some optimizations) whatsoever. It chiefly serves self-documenting/error-preventing purposes, much like IMPLICIT NONE. So it's best left out when discussing mixed-language issues.

Yes, Fortran string arguments should be declared ByVal; that's fairly odd VB convention. As I recall, ByRef roughly corresponds to Fortran CHARACTER(), POINTER, i.e. the called routine may reallocate storage for it. Did that alone solve the problem?

No. I cannot get the string variable back into VB, no matter what I do. Interestingly, the Mixed Language example in the IVF folders works, and even when I change the text string it is passed back correctly. In my program, with the same combination of ByVal and REFERENCE attributes, nothing seems to enable me to pass a string back.

D
0 Kudos
anthonyrichards
New Contributor III
1,150 Views
Surely, if you want to change the contents of a string, you must have a reference to the location of the string buffer, so its address must be passed to Fortran, hence REFERENCE and a pointer used as the matching argument on the VB side?
0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,150 Views
Quoting - David White

No. I cannot get the string variable back into VB, no matter what I do. Interestingly, the Mixed Language example in the IVF folders works, and even when I change the text string it is passed back correctly. In my program, with the same combination of ByVal and REFERENCE attributes, nothing seems to enable me to pass a string back.


a) What happens if you write a dummy Fortran routine with just a string argument?

b) I think you must initialize the string in VB (or at least assign it a length, not sure how).
0 Kudos
anthonyrichards
New Contributor III
1,150 Views
For what it's worth, here is a VB6 projectthat callssubroutines from two FortranDLLS. The CVF Fortran workspace used to generate the 2 DLL's is included. One calls a subroutine tosend a VB string to a Fortran DLL, the other calls a function to supply a string from a Fortran DLL, the function value returning the string length. The subroutine call assumes and uses the 'hidden' string length. The function callsupplies a pointer (address reference) to a VB buffer and a buffer length to Fortran. The Fortran copies a string to the supplied buffer addressusing CopyMemory and returns the copied string length as the function value. Care is taken to avoid buffer overrun.

I have found thatalthough Visual Basic stores strings as unicode (2 bytes per character) it automatically
converts from unicode to Ascii when sending strings to external code (as indicated by the Declare
statement in the VB code) and when receiving strings backfrom such external code (such as C,C++ and,
in the present case, Fortran).

I include screen shoots taken from debug sessions showing memory contents at critical points and illustrating that the character strings sent and received are as expected.

0 Kudos
DavidWhite
Valued Contributor II
1,150 Views
Thanks, Anthony, I'll look at your example when I get back to work tomorrow.

The maddening thing about this is that I tried the IVF mixed language example, even modified it so that I change the text string in the DLL and it is returned to the VB side without any problems. My code, which has the same combinations of ByRef and REFERENCE now throws an VB error saying that the unmanaged code has unbalanced the stack.

D
0 Kudos
Lorri_M_Intel
Employee
1,150 Views
"unbalanced the stack" ... that might be a clue. If I recall correctly, VB's default calling sequence is {nearly} STDCALL. IVF's default is now C. With STDCALL, the callee cleans up the stack; with C the caller cleans up the stack.

Sounds like there may be a mismatch in expectations?

- Lorri
0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,150 Views
Quoting - lorri_menard
"unbalanced the stack" ... that might be a clue. If I recall correctly, VB's default calling sequence is {nearly} STDCALL. IVF's default is now C. With STDCALL, the callee cleans up the stack; with C the caller cleans up the stack.

Sounds like there may be a mismatch in expectations?


Unlikely, because !DEC$ATTRIBUTES STDCALL is spelled out, as indicated in David's first post. With the stdcall/cdecl mismatch, not only the string arguments would go wrong.
0 Kudos
Jugoslav_Dujic
Valued Contributor II
1,150 Views
Quoting - David White
Thanks, Anthony, I'll look at your example when I get back to work tomorrow.

The maddening thing about this is that I tried the IVF mixed language example, even modified it so that I change the text string in the DLL and it is returned to the VB side without any problems. My code, which has the same combinations of ByRef and REFERENCE now throws an VB error saying that the unmanaged code has unbalanced the stack.


David, we don't see your actual code, so we can only guess what happened. So, until you share it with us, this will be my last attempt at guessing :-).

According to http://msdn.microsoft.com/en-us/library/thwcx436.aspx, The default value of String is Nothing (a null reference). Note that this is not the same as the empty string (value ""). Now, if you just declare String in VB, and not initialize it, it will get passed to Fortran as a null reference. Your declaration of CHARACTER(20) does not change an iota -- it says to Fortran that the actual argument should be a string of no less than 20 characters, but it cannot change the fact that it's not. The first time you try to put anything in it, it will go kaboom.

At least, initialize the string in VB to something at least 20-long, test its value in Fortran first, and only then if you get the crash on write again, we will have a real Gremlin to deal with.

0 Kudos
DavidWhite
Valued Contributor II
1,150 Views
Quoting - Jugoslav Dujic

David, we don't see your actual code, so we can only guess what happened. So, until you share it with us, this will be my last attempt at guessing :-).

According to http://msdn.microsoft.com/en-us/library/thwcx436.aspx, The default value of String is Nothing (a null reference). Note that this is not the same as the empty string (value ""). Now, if you just declare String in VB, and not initialize it, it will get passed to Fortran as a null reference. Your declaration of CHARACTER(20) does not change an iota -- it says to Fortran that the actual argument should be a string of no less than 20 characters, but it cannot change the fact that it's not. The first time you try to put anything in it, it will go kaboom.

At least, initialize the string in VB to something at least 20-long, test its value in Fortran first, and only then if you get the crash on write again, we will have a real Gremlin to deal with.

The string was padded with 20 blanks on the VB side before the call.

I gave the declarations for the VB and Fortran sides in my first post. I'll post the rest when I get back on my main machine.
0 Kudos
sabalan
New Contributor I
1,150 Views
David,
I have written lots of mixed language codes calling Fortan (CVF) DLLs from VB (4, 5, 6, .NET) calling back VB from Fortran (to show a status bar etc.), with arrays, strings, string arrays, logicals (Boolean), and also calling Fortran DLLs from Excel. I had similar problems in the beginning (DVF 5.0 and VB4). The best way I found, and have never had such problems since then, is the following:

1- Declare all numerical actual arguments at the VB-side as arrays with the necessary dimensions or otherwise with dimension 1: Dim MyVar(1 to 1) As Double;
2- Declare the strings as usual: Dim MyString*255;
3- Use ByVal for strings and Booleans only in the DLL declaration.

On the Fortran side:
1- Use !DEC$ ATTRIBUTES REFERENCE:: for strings and logicals only;
2- Declare numerical dummy arguments as arrays of dimension 1 here also (if they are not bigger arrays).

And it would do it!

Sabalan

0 Kudos
DavidWhite
Valued Contributor II
1,150 Views
Quoting - sabalan
David,
I have written lots of mixed language codes calling Fortan (CVF) DLLs from VB (4, 5, 6, .NET) calling back VB from Fortran (to show a status bar etc.), with arrays, strings, string arrays, logicals (Boolean), and also calling Fortran DLLs from Excel. I had similar problems in the beginning (DVF 5.0 and VB4). The best way I found, and have never had such problems since then, is the following:

1- Declare all numerical actual arguments at the VB-side as arrays with the necessary dimensions or otherwise with dimension 1: Dim MyVar(1 to 1) As Double;
2- Declare the strings as usual: Dim MyString*255;
3- Use ByVal for strings and Booleans only in the DLL declaration.

On the Fortran side:
1- Use !DEC$ ATTRIBUTES REFERENCE:: for strings and logicals only;
2- Declare numerical dummy arguments as arrays of dimension 1 here also (if they are not bigger arrays).

And it would do it!

Sabalan

Sabalan,

Still a couple of issues with this. The version of VB with VS2005 only permits arrays to start at element 0 and the *255 form of declaring a string length is not permitted either.

However, changing your arrary declarations to X(0 to 0) and the string to "var as String"seems to be working -- will pass onto my customer to see if he can replicate.

David
0 Kudos
DavidWhite
Valued Contributor II
1,150 Views
Revising Sabalan's suggestion, solution appears to be (same entry points can be used for VBA and VB):

on VB side
1. Declare numerical variables asusual as either scalars or arrays as required; these are passed ByRef
2. Declare string variables as usual (possibly pad with blanks to required length); these are passed ByVal

on Fortran side
1. Declare dummy arguments (including scalar real arguments) as arrays of dimension (1) or actual dimension if larger
2. Use !DEC$ ATTRIBUTES REFERENCE :: for string arguments only

Thanks for everyone's assistance

David
0 Kudos
Reply