I'm trying to modify the Safearray example for passing multi-dimensional string arrays from VB.Net to Fortran. I want to be able to pass large REAL*8 arrays using Safearray. I have a program that exceeds the stack storage to due large array sizes and I want to pass them using Safearrays. I modified the example but I can't seem to get SafeArrayGetElement statement to retrieve values from the passed in array. The code below shows the VB calling routine with the FORTRAN routine below. SafeArrayGetElement returns 0 for the array index provided. I intend to use a pointer to CurElem to get the value and work on it.
Any help would be appreciated.
Imports System.Runtime.InteropServices ' Required for using MarshalAs Public Class Form1 ' Declare the Fortran routine. The name of the routine is case-sensitive ' and the path to the DLL is explicit. When run from Visual Studio, the DLL ' is looked for in the BIN subfolder of the VB.NET project ' ' By default, VB.NET would simply pass the data to the DLL without any bounds ' information (and no chance of rewriting the strings). So we use the MarshalAs ' attribute to tell it to pass a SafeArray. If you were passing numeric types, ' you could just pass the data and access it as a normal array. ' Private Declare Sub ForCall Lib "SafeArrayFORTRAN.dll" _ (<MarshalAs(UnmanagedType.SafeArray)> ByRef array1(,) As Double) Private myarray(2, 3) As Double Private Sub _cmdRun_Click(sender As Object, e As EventArgs) Handles _cmdRun.Click myarray(0, 0) = 12 myarray(0, 1) = 11 myarray(0, 2) = 10 myarray(0, 3) = 9 myarray(1, 0) = 8 myarray(1, 1) = 7 myarray(1, 2) = 6 myarray(1, 3) = 5 myarray(2, 0) = 4 myarray(2, 1) = 3 myarray(2, 2) = 2 myarray(2, 3) = 1 Call ForCall(myarray) End Sub End Class Subroutine ForCall(VBArray) !DEC$ ATTRIBUTES DLLEXPORT,REFERENCE,STDCALL::ForCall !DEC$ ATTRIBUTES ALIAS: 'ForCall'::ForCall !DEC$ ATTRIBUTES REFERENCE :: VBArray use ifcom ! Declare SafeArray and BSTR interfaces implicit none integer(int_ptr_kind()), intent(inout) :: VBArray !Pointer to a SafeArray structure INTEGER(INT_PTR_KIND()) :: ptrElem REAL*8,TARGET :: CurElem POINTER(ptrElem, CurElem) ! This says that ptrElem holds the address of CurElem. !> Array in which we will keep track of array bounds type bounds_type integer lb ! Lower Bound integer ub ! Upper Bound end type bounds_type !< integer nbounds ! Number of bounds type(bounds_type), allocatable :: bounds(:) integer, allocatable :: indexes(:) ! Array to hold current element indexes integer :: i, j, iRes nbounds = SafeArrayGetDim (VBArray) allocate (bounds(nbounds), indexes(nbounds)) do i=1,nbounds ires = SafeArrayGetLbound (VBArray, i, bounds(i)%lb) ires = SafeArrayGetUbound (VBArray, i, bounds(i)%ub) end do DO j=bounds(1)%lb,bounds(1)%ub DO i=bounds(2)%lb,bounds(2)%ub indexes(1)=i+1 indexes(2)=j+1 ires = SafeArrayGetElement (VBArray, indexes(1), loc(ptrElem)) !OUTPUT CurElem HERE end do end do deallocate (bounds) deallocate (indexes) return end subroutine ForCall
Ok, I found the solution to my original problem, which was a Stack Overflow Exception when passing large arrays to my FORTRAN dll. I was trying to correct the issue by passing SafeArrays. A much simpler approach is to change the Heap Array parameter to 0. The answer came from here: https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/279050