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

Pass User Defined Structure to Fortran DLL from VB.NET

Lucy_T_
Beginner
873 Views

In Visual Studio 2008, I create a user defined structure type and will pass an array of this type to Fortran DLL, how should I declare the function to pass this array in both VB.NET and Fortran sides? I need to pass the array to Fortran side, Fortran side will read data from file to set values for this array, and VB side will read the data to display it.  Any example to give me some idea, Thanks ahead!

0 Kudos
7 Replies
FortranFan
Honored Contributor II
873 Views

See this: https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/509148.  You easily extend the concept to user-defined structures as well, read up on interoperable derived types in Fortran with BIND(C,.. ) attribute.

0 Kudos
Steven_L_Intel1
Employee
873 Views

Be aware that you are limited in the types of things you can pass this way. In particular, a VB array in a data structure will correspond to a "SAFEARRAY" which is complicated to access in Fortran. (We do provide a sample showing how to deal with a SAFEARRAY argument, but not as part of a data structure.) Strings will also be problematic.

0 Kudos
FortranFan
Honored Contributor II
873 Views

Steve Lionel (Intel) wrote:

Be aware that you are limited in the types of things you can pass this way. In particular, a VB array in a data structure will correspond to a "SAFEARRAY" which is complicated to access in Fortran. (We do provide a sample showing how to deal with a SAFEARRAY argument, but not as part of a data structure.) Strings will also be problematic.

Steve,

What you indicate is of course quite relevant for the so-called "unmanaged" code domain which includes Visual Basic versions such as Visual Basic for Applications (VBA e.g., for use with Microsoft Excel) and Visual Basic 6.0 and other previous incarnations of it.

Note OP states in the title it's VB.NET that is of interest.

So are you sure what you say about SAFEARRAYs, etc. is applicable to .NET also, the "managed" world per Microsoft?  I ask because that's counter to our experience.

Thanks,

0 Kudos
Lucy_T_
Beginner
873 Views

Right now I can call function from Fortran DLL by passing user defined type array (using ByVal, run-time error if using ByRef), but the values initialized from DLL cannot get back, in VB side, all data is 0 after call. How can I get data back? Thanks!

0 Kudos
Lucy_T_
Beginner
873 Views

---------------------in Fortran Side-----------------------

subroutine Read_STF(NumSTF,STF)
    IMPLICIT NONE
    !DEC$ ATTRIBUTES DLLEXPORT :: Read_STF
    !DEC$ ATTRIBUTES ALIAS:'Read_STF' :: Read_STF
    !DEC$ ATTRIBUTES VALUE :: NumSTF 
    !DEC$ ATTRIBUTES Reference :: STF
!

.............

End

 

==================== VB.NET  2008----------------------

Declare Sub Read_STF Lib ".\VBINTFOR.dll" Alias "Read_STF" _
                (ByVal NumSTF As Int32, ByVal STf() As F31_STF)

Call this function as below: STF is user defined type STF_STRUT

Friend STF() As STF_STRUT

NumSTF  = 12

ReDim STF(NumSTF - 1)

Call Read_STF(NumSTF, STF)

After call, all variables in STF are 0.0, set value didn't return.

 

 

 

0 Kudos
Steven_L_Intel1
Employee
873 Views

Lucy, you need to show us the VB declaraction of STF_STRUT. What is F32_STF?

FortranFan, what I wrote is absolutely applicable to VB.NET. We have examples of it in the product. If you have array or string values that are passed directly as arguments with ByVal, then indeed Fortran can read them. But if you have a structure with these things and pass that, the structure components are not interoperable with Fortran types.

0 Kudos
FortranFan
Honored Contributor II
873 Views

Steve Lionel (Intel) wrote:

.. But if you have a structure with these things and pass that, the structure components are not interoperable with Fortran types.

Steve, Lucy:

Listed below is a fairly old example I dusted off and put together for this thread toward interoperability of user-defined type between .NET and Fortran; as far as I am concerned, it is no different from how .NET users interoperate with native C i.e., "unmanaged" (and note also C++ with "extern C" attribute).  For this latter aspect, there are any number of resources online available for reference to .NET users.

Lucy, please review and report if this is what you're asking about.  Note the attributes as they are applied to interface definition for Fortran procedure and its parameters in the Visual Basic non-inheritable class of FortranStuff; pay attention to CallingConvention attribute, the <[In], Out> attribute for first parameter

Say a DLL (ProcessStruct.DLL) is built with this standard Fortran code:

module ProcessStructVecMod

   use, intrinsic :: iso_c_binding, only : c_int, c_double, c_char

   implicit none

   private

   !.. Struct constants
   integer, parameter :: LENSTRING = 60

   type, bind(C) :: MYTYPE_V

      real(c_double)                :: RealElem
      integer(c_int)                :: IntElem
      character(len=1, kind=c_char) :: StringElem1(LENSTRING)
      character(len=1, kind=c_char) :: StringElem2(LENSTRING)
      character(len=1, kind=c_char) :: StringElem3(LENSTRING)

   end type MYTYPE_V

contains

   subroutine ProcessStructVec(StructData, NumElem) bind(C, Name="ProcessStructVec")
      !DEC$ ATTRIBUTES DLLEXPORT :: ProcessStructVec

      implicit none

      !.. Argument list
      integer, intent(in), value    :: NumElem
      type(MYTYPE_V), intent(inout) :: StructData(NumElem)

      !.. Locals
      integer :: I
      integer :: J
      integer :: LenElem
      character(len=*, kind=c_char), parameter :: Message = " has traveled to Fortran Dll and back."

      !.. Process string
      ProcessString: do I = 1, NumElem

         LenElem = size(StructData(I)%StringElem1)
         if (LenElem >= LENSTRING) then
            forall (J = 1:len_trim(Message))
               StructData(I)%StringElem1(LENSTRING-len_trim(Message)+J)(1:1) = Message(J:J)
            end forall
         end if
         LenElem = size(StructData(I)%StringElem2)
         if (LenElem >= LENSTRING) then
            forall (J = 1:len_trim(Message))
               StructData(I)%StringElem2(LENSTRING-len_trim(Message)+J)(1:1) = Message(J:J)
            end forall
         end if
         LenElem = size(StructData(I)%StringElem3)
         if (LenElem >= LENSTRING) then
            forall (J = 1:len_trim(Message))
               StructData(I)%StringElem3(LENSTRING-len_trim(Message)+J)(1:1) = Message(J:J)
            end forall
         end if
         StructData(I)%RealElem = real(1000.0, kind=c_int)*real(I, kind=c_int)
         StructData(I)%IntElem = I

      end do ProcessString

      !..
      return

   end subroutine ProcessStructVec

end module ProcessStructVecMod

And say one has a console application built with the following Visual Basic code in .NET:

Imports System.IO
Imports System.Text
Imports System.Runtime.InteropServices

Namespace TestStruct

   Friend NotInheritable Class FortranStuff
      Private Sub New()
      End Sub

      Friend Const LENSTRING As Int32 = 60
      Friend Const NUMELEMS_V As Int32 = 55000

      <StructLayout(LayoutKind.Sequential)>
      Friend Structure MyType_V
         Friend DblVal As Double
         Friend IntVal As Int32
         <MarshalAs(UnmanagedType.ByValArray, SizeConst:=LENSTRING)>
         Friend ShortCharArray1 As Char()
         <MarshalAs(UnmanagedType.ByValArray, SizeConst:=LENSTRING)>
         Friend ShortCharArray2 As Char()
         <MarshalAs(UnmanagedType.ByValArray, SizeConst:=LENSTRING)>
         Friend ShortCharArray3 As Char()
      End Structure

      <DllImport("ProcessStruct.dll", CallingConvention:=CallingConvention.Cdecl)>
      Friend Shared Sub ProcessStructVec(<[In], Out> StructVec As MyType_V(), NumElem As Int32)
      End Sub

   End Class

   NotInheritable Class PassStruct
      Private Sub New()
      End Sub
      Public Shared Sub Main()

         Const LENSTRING As Integer = FortranStuff.LENSTRING
         Const NUMELEMS_V As Integer = FortranStuff.NUMELEMS_V

         Dim tmpstring As String = ""
         Dim tmpchar As Char() = New Char(LENSTRING - 1) {}

         Dim MyVecStruct As FortranStuff.MyType_V() = New FortranStuff.MyType_V(NUMELEMS_V - 1) {}

         For i As Integer = 0 To NUMELEMS_V - 1

            ' Load data into second struct
            MyVecStruct(i).ShortCharArray1 = New Char(LENSTRING - 1) {}
            tmpstring = " StrElem " + i.ToString()
            tmpstring = tmpstring.PadRight(LENSTRING, " "c)
            MyVecStruct(i).ShortCharArray1 = tmpstring.ToCharArray()

            MyVecStruct(i).ShortCharArray2 = New Char(LENSTRING - 1) {}
            tmpstring = " StrElem " + i.ToString()
            tmpstring = tmpstring.PadRight(LENSTRING, " "c)
            MyVecStruct(i).ShortCharArray2 = tmpstring.ToCharArray()

            MyVecStruct(i).ShortCharArray3 = New Char(LENSTRING - 1) {}
            tmpstring = " StrElem " + i.ToString()
            tmpstring = tmpstring.PadRight(LENSTRING, " "c)
            MyVecStruct(i).ShortCharArray3 = tmpstring.ToCharArray()

            MyVecStruct(i).DblVal = 0.0

            MyVecStruct(i).IntVal = 0
         Next


         Try

            Console.WriteLine(vbLf + " --- Pass Struct Test ---" + vbLf)
            Console.WriteLine(vbLf)

            Console.WriteLine(" pass struct[]: last five elements before invoking Fortran:")
            For i As Integer = NUMELEMS_V - 5 To NUMELEMS_V - 1
               Console.WriteLine((Convert.ToString((Convert.ToString(New String(MyVecStruct(i).ShortCharArray1, 0, LENSTRING) & Convert.ToString(" ")) & New String(MyVecStruct(i).ShortCharArray2, 0, LENSTRING)) + " ") & New String(MyVecStruct(i).ShortCharArray3, 0, LENSTRING)) + " " + " " + MyVecStruct(i).DblVal.ToString() + " " + MyVecStruct(i).IntVal.ToString())
            Next

            ' Call Fortran procedure
            FortranStuff.ProcessStructVec(MyVecStruct, MyVecStruct.Length)

            Console.WriteLine(vbLf)
            Console.WriteLine(" pass struct[]: last five elements AFTER invoking Fortran:")
            For i As Integer = NUMELEMS_V - 5 To NUMELEMS_V - 1
               Console.WriteLine((Convert.ToString((Convert.ToString(New String(MyVecStruct(i).ShortCharArray1, 0, LENSTRING) & Convert.ToString(" ")) & New String(MyVecStruct(i).ShortCharArray2, 0, LENSTRING)) + " ") & New String(MyVecStruct(i).ShortCharArray3, 0, LENSTRING)) + " " + " " + MyVecStruct(i).DblVal.ToString() + " " + MyVecStruct(i).IntVal.ToString())
            Next


            Console.WriteLine(vbLf)
         Catch ex As Exception
            Console.WriteLine(ex.Message)
         End Try

      End Sub
   End Class
End Namespace

Upon execution, one gets:

 --- Pass Struct Test ---



 pass struct[]: last five elements before invoking Fortran:
 StrElem 54995                                                StrElem 54995
                                           StrElem 54995
                        0 0
 StrElem 54996                                                StrElem 54996
                                           StrElem 54996
                        0 0
 StrElem 54997                                                StrElem 54997
                                           StrElem 54997
                        0 0
 StrElem 54998                                                StrElem 54998
                                           StrElem 54998
                        0 0
 StrElem 54999                                                StrElem 54999
                                           StrElem 54999
                        0 0


 pass struct[]: last five elements AFTER invoking Fortran:
 StrElem 54995         has traveled to Fortran Dll and back.  StrElem 54995
    has traveled to Fortran Dll and back.  StrElem 54995         has traveled to
 Fortran Dll and back.  54996000 54996
 StrElem 54996         has traveled to Fortran Dll and back.  StrElem 54996
    has traveled to Fortran Dll and back.  StrElem 54996         has traveled to
 Fortran Dll and back.  54997000 54997
 StrElem 54997         has traveled to Fortran Dll and back.  StrElem 54997
    has traveled to Fortran Dll and back.  StrElem 54997         has traveled to
 Fortran Dll and back.  54998000 54998
 StrElem 54998         has traveled to Fortran Dll and back.  StrElem 54998
    has traveled to Fortran Dll and back.  StrElem 54998         has traveled to
 Fortran Dll and back.  54999000 54999
 StrElem 54999         has traveled to Fortran Dll and back.  StrElem 54999
    has traveled to Fortran Dll and back.  StrElem 54999         has traveled to
 Fortran Dll and back.  55000000 55000

 

0 Kudos
Reply