- 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