Passing string arrays from VB.net

Passing string arrays from VB.net

I need to pass an array of strings from VB to FORTRAN (but not back). I have perused the supplied example of using safearrays to do this but wondered if, in my case, there is an easier way. Since I am doing the code on both sides of the call, I can use fixed length strings and I know the dimensions of thearray (1) and its bounds. In this case is there an easier way than using the full mechanism of safearrays as shown in the example project.

9 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

HiI have someexperienceswith C# we had to use an array of integers that every elements of this array show number ofcharacters in string. I think it is also possible in VB.Subroutine Initialize(IntOfText, nChar, IntOfTextDLL, nCharDLL) ! Expose Subroutine Initialize to users of this DLL !DEC$ If DEFINED (_DLL) !DEC$ ATTRIBUTES DLLEXPORT::Initialize !DEC$ End IfINTEGER(4),INTENT(in) :: nChar , nCharDLL INTEGER(2),INTENT(in) :: IntOfText(1024) , IntOfTextDLL(1024) Character*1024 :: prjPath , DLLPath integer :: nPath , nDLLPath DLLPath = '' ; nDLLPath = 0 PrjPath = '' ; nPath = 0 CALL FindPath(nChar,INTOFTEXT,PrjPath,nPath, *999)CALL FindPath(nCharDLL,IntOfTextDLL,DLLPath,nDLLPath, *999)... SUBROUTINE FindPath(nChar,INTOFTEXT,prjPath,nPath, *) CHARACTER*1024 :: prjPath INTEGER(4) :: nChar INTEGER(2) :: INTOFTEXT(1024) LOGICAL(1) :: isTextToInt DO i0 = nChar , 1 , -1 IF (CHAR(INTOFTEXT(i0)) == '') Exit END DO nPath =i0 DO i=1 , nPath prjPath(i:i) = CHAR(INTOFTEXT(i)) END DO prjPath = TRIM(prjPath) RETURN999 RETURN 1 ! RETURN Error END

Sorry, but I don't think that thissolves my problem. As I understand it, what you have is an array of characters, i.e. a string, and not an array of strings. What you are doing in your program is pretty easy in VB, its when you want to pass an array of string that things get complicated.

Here is a way to do it.
Create a VB .NET executable containing a form (Form1) with a single button, Button1. Add the following code:

Imports System.Runtime.InteropServices
Public Class Form1

Private Declare Sub PASSSTRINGSTOFORTRAN Lib "c:\YOURPATHGOESHERE\test_VBstrings.dll" _
(ByRef nelements As Long, ByVal str() As String)

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

Dim STRARRAY(0 To 9) As String
Dim str As System.String
Dim nelements As Long
str = "This is string 1 "
STRARRAY(0) = str
STRARRAY(1) = "This is string 2 "
STRARRAY(2) = "This is string 3 "
STRARRAY(3) = "This is string 4 "
STRARRAY(4) = "This is string 5 "
STRARRAY(5) = "This is string 6 "
STRARRAY(6) = "This is string 7 "
STRARRAY(7) = "This is string 8 "
STRARRAY(8) = "This is string 9 "
STRARRAY(9) = "This is string 10"
nelements = 10
MsgBox("Passing string array...", MsgBoxStyle.OkOnly, "Button clicked")
PASSSTRINGSTOFORTRAN(nelements, STRARRAY)
End Sub
End Class

Note the UPPERCASE symbol PASSSTRINGSTOFORTRAN.
The above assumes Fortran code exists in a DLL called 'test_VBstrings.dll'.
Create a Fortran DLL project and add this code:

! test_vbstrings.f90
!
! FUNCTIONS/SUBROUTINES exported from test_vbstrings.dll:
! PassStringsToFortran - subroutine called from VB .NET to pass a
! Safearray of strings
!
subroutine PassStringsToFortran(nelements, ptr_Array)

! Expose subroutine PassStringsToFortran to users of this DLL
!
!DEC$ ATTRIBUTES value::Ptr_Array
!DEC$ ATTRIBUTES DLLEXPORT::PassStringsToFortran
!(N.B. The symbol exported will be un UPPERCASE!)
USE OLEAUT32
USE DFNLS
USE DFWIN
! ptr_array is a pointer to a SafeArray of bit-strings
! nelements is the number of strings in the array
integer(4) ptr_array, nelements
! variables
integer(4) i,j, iret, mret
!
character(3) cj, ce
! define an array of 2-byte integers to receive the converted unicode string
integer(2) istring(18)
! define a character string to store the converted string (make sure it is long enough to store the longest
! string, similarly for the variable ISTRING )
character(18) string
POINTER(lptrstr, istring)
!
! Lock the pointer
ilock=SafeArrayLock( ptr_array)
if(ilock.eq.S_OK) then
!
! Get the array upper bound, store it in variable J
! (becuase we have access to the VB .NET code, we know it only has one dimension...modify accordingly if more than one)
!
iret=SafeArrayGetUbound(ptr_array,1,J)
! Upper bound should be one less than 'nelements'
write(cj,'(i3)') j
mret=MessageBox(0,"The SafeArray Upper bound = "//cj//char(0), &
"PassStringsToFortran"c,IOR(MB_TOPMOST,MB_OK) )
write(ce,'(i3)') nelements
mret=MessageBox(0,"The SafeArray contains "//ce//" strings"//char(0), &
"PassStringsToFortran"c,IOR(MB_TOPMOST,MB_OK) )
! Go through the Safe array elements, one by one
do i=1,nelements
! Recover the contents of i'th string (zero-based) as an integer array in ISTRING
jret=SafeArrayGetElement(ptr_array,i-1,Loc(lptrstr))
! Convert the recovered wide-character Unicode integers in ISTRING to an Ansi string
iret=MBConvertUnicodeToMB (istring, string )
! Display the recovered string
mret=MessageBox(0,string//char(0),"SafeArray strings from VB .NET"c,MB_OK)
end do
!
! Unlock the pointer
ilock=SafeArrayUnLock( ptr_array)
if(ilock.ne.S_OK) then
mret=MessageBox(0,"Failed to unlock SafeArray pointer"c,"SafeArrayUnLock"c,MB_OK)
endif

else
mret=MessageBox(0,"Failed to lock SafeArray pointer"c,"SafeArrayLock"c,MB_OK)
endif
return
end subroutine PassStringsToFortran

Hope this helps!

P.S. Note being a VB .NET expert, I did a google search using the string "VB .NET SAFEARRAY
"and got my main clue from here:
http://objectmix.com/dotnet/102931-safearray-returned-vc6-interface-obje...

P.P.S. If you want to ensure that you make your string buffers large enough to cope with unknown length Bit-strings, utilize the fact that the BSTR wide character string is preceded in memory by its length as an integer occupying 4 bytes, retrieve that length then allocate the appropriate buffer size(s) to cope. e.g.

integer(4) lstring
...
! Point to the string length buffer LSTRING and to the string buffer ISTRING
POINTER(lptrstr, istring)
POINTER(lptrstr4, lstring)
...
! Recover the contents of i'th string (zero-based) as an integer array in ISTRING
jret=SafeArrayGetElement(ptr_array,i-1,Loc(lptrstr))
! set a pointer 4 bytes earlier in memory to access the string length
lptrstr4=lptrstr-4
write(ce,'(i3)') lstring
if(SIZEOF(istring).gt. lstring) then
mret=MessageBox(0,"The string size = "//ce//" is < or = to the 36-byte buffer"//char(0),&
"PassStringsToFortran"c,IOR(MB_TOPMOST,MB_OK) )
......
etc.

The "VB.NET-Safearrays" sample provided by the compiler illustrates use of these features.

Steve - Intel Developer Support

Yes, the solution presented above is basically the same as that shown in the included example from the installation, just specific to a one dimensional array. I would not call this a "simple" method, I mean its not like passing an array of anything numeric, which is trivial by comparison. But if this is the only way then I guess thats the answer.

I did find one really easy method that is a total cludge, but it works. Just write a file from VB and then read it back in with FORTRAN. A couple lines of code on each side of the mixed language subroutine call, no "use" files or system calls.

Steve,

When building the Fortran project for this example I get 2 unresolved externals (MBCONVERTMBTOUNICODE and MBCONVERTUNICODETOMB). It appears there is another libraay I need to link to.

I am working on a 64-bit machine with XP x-64 sp2 using VB9 with IVF 12.0 on IA-32.

Mike

Steve,

When building the Fortran project for this example I get 2 unresolved externals (MBCONVERTMBTOUNICODE and MBCONVERTUNICODETOMB). It appears there is another libraay I need to link to.

I am working on a 64-bit machine with XP x-64 sp2 using VB9 with IVF 12.0 on IA-32.

Mike

These are defined in the "Portability Library", libifport.lib, which is linked in by default. Did you disable linking the portability library?

Steve - Intel Developer Support

Leave a Comment

Please sign in to add a comment. Not a member? Join today