- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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!
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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,
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
---------------------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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page