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

Stack Error Passing a string from VB.NET to a Fortran DLL

Mark_S_4
Beginner
1,083 Views

Hi,

I am rewriting a large application moving from VB6 using FORTRAN executables, to VB.NET using FORTRAN DLL's.

I am able to move single values and arrays both to and from the DLL, but now have gotten stuck trying to push strings. 

Here is the relevant VB.NET code:

In the declarations section:

    <UnmanagedFunctionPointer(CallingConvention.StdCall)>
Private Delegate Sub pushString(ByRef varIndex As Short, ByVal MsgString As StringBuilder) 

        Dim EntryPointer As IntPtr = NativeMethods.LoadLibrary(EXE_DLL_filename)
        FunctionPointer = NativeMethods.GetProcAddress(EntryPointer, "pushString")
        Dim pushString As pushString = CType(Marshal.GetDelegateForFunctionPointer(FunctionPointer, GetType(pushString)), pushString)

Executable code:

            Dim index As Short = 501
            Dim filePath As String = "C:\Users\mark.ARROWTECH\AppData\Local\Temp\tmpD33F.tmp"
            Dim strMessage As StringBuilder = New StringBuilder(filePath, 120)
            pushString(index, strMessage)

The DLL that I link to is only known at runtime.


Relevant FORTRAN code:

!dec$ attributes dllexport, CVF,  alias : 'pushString' :: pushString
SUBROUTINE pushString(varIndex, InputString )
use, intrinsic :: ISO_C_BINDING
USE Traj20006D_Global_Export
integer(2), intent(in) :: varIndex
CHARACTER(KIND=C_CHAR,LEN=1), INTENT(in) :: InputString(*)
INTEGER :: StringLen
INTEGER :: I
 !.. Determine input string length; check for null-termination
 StringLen = 0
 Loop_SizeVec: DO !
  IF (InputString(StringLen+1) == C_NULL_CHAR) THEN
   EXIT Loop_SizeVec
  End If
  StringLen = StringLen + 1
  IF (StringLen == 2048) THEN !.. Replace: arbitrary large length
   EXIT Loop_SizeVec
  END IF
 END DO Loop_SizeVec
 IF (StringLen == 0) RETURN
 select case(varIndex)
  Case (501)
   FORALL (I = 1:StringLen)     !.. Copy string
    TEXTFILE1(I:I) = InputString(I)(1:1)
   END FORALL
  Case (502)
   FORALL (I = 1:StringLen)     !.. Copy string
    TEXTFILE2(I:I) = InputString(I)(1:1)
   END FORALL
  End Select
 Return
END SUBROUTINE pushString

So what happens is the string is transferred fine, but on leaving the routine I get this error:

"Additional information: A call to PInvoke function 'PRODASV4!PRODASV4.prodasTask+pushString::Invoke' has unbalanced the stack. "

I am just not sure what to try next.

Thanks,
Mark

 

0 Kudos
1 Solution
FortranFan
Honored Contributor III
1,083 Views

My hunch, based on a very quick glance at your code, is your problem is !DEC$ ATTRIBUTES specification of CVF.  You may want to:

  1. Simply specify DEC$ ATTRIBUTES DLLEXPORT::pushString
  2. have the procedure declaration as SUBROUTINE pushString(varIndex, InputString ) BIND (C, Name="pushString")

See this thread: https://software.intel.com/en-us/forums/topic/509148.

My suggestion would be that if you are going to do mixed-language solution development, make full use of C interoperability features in Fortran via ISO_C_BINDING.  Look into all the data types e.g., Short in Visual Basic vis-a-vis C_SHORT in ISO_C_BINDING; review all the procedure interop aspects relative to BIND keyword, etc.  And avoid "mixed mode" that you have currently, where you make part use of ISO_C_BINDING and mix it with Intel Fortran extensions of !DEC$ attributes.  Mixed types (e.g., INTEGER(2)) may also cause problems.

View solution in original post

0 Kudos
2 Replies
FortranFan
Honored Contributor III
1,084 Views

My hunch, based on a very quick glance at your code, is your problem is !DEC$ ATTRIBUTES specification of CVF.  You may want to:

  1. Simply specify DEC$ ATTRIBUTES DLLEXPORT::pushString
  2. have the procedure declaration as SUBROUTINE pushString(varIndex, InputString ) BIND (C, Name="pushString")

See this thread: https://software.intel.com/en-us/forums/topic/509148.

My suggestion would be that if you are going to do mixed-language solution development, make full use of C interoperability features in Fortran via ISO_C_BINDING.  Look into all the data types e.g., Short in Visual Basic vis-a-vis C_SHORT in ISO_C_BINDING; review all the procedure interop aspects relative to BIND keyword, etc.  And avoid "mixed mode" that you have currently, where you make part use of ISO_C_BINDING and mix it with Intel Fortran extensions of !DEC$ attributes.  Mixed types (e.g., INTEGER(2)) may also cause problems.

0 Kudos
Mark_S_4
Beginner
1,083 Views

Thanks for the quick response. 

I modified my FORTRAN per your suggestions, and also cleaned up the data types.  In addition, I changed the calling convention from STDCALL to CDECL in the VB.NET code.

Works like a champ,

Thanks again

0 Kudos
Reply