Topic: Fortran DLL for VBA / VB6 programs

Hi ,
i get a little bit frustrating to create a dll for vba - cause sometimes i might have the need for parameters ...

Ok the testcode first to keep it simple:
Fortran:
function testme()
    integer testme
    testme=999
end

function testi(i,j)
integer i
integer j
integer testi       
    testi =j
end


testme works testi doesnt

COMPILER PARAMETERS:
Fortran Compiler : -fno-underscoring
C Compiler :-fstdcall  -add-stdcall-alias -shared
Linker : -mrtd - shared


Excel VBA :
Declare Function testme Lib "M:\DEVEL\fortran-library.dll" () As Long
Declare Function testi Lib "M:\DEVEL\fortran-library.dll" (i As Long, j As Long) As Long

Option Base 1
Sub fortran()

    Dim i As Long
    Dim j As Long
    Dim k As Long
   
    k = testme()
    Debug.Print k 'works
   
    i = 1
    j = 2
   
    Debug.Print testi(i, j)  'should return 2 - wrom dll calling conventoin error or crash
End Sub

I guess i am not the only one who has to fight with VBA sometimes and no i am not a C++ guru
but you can have some nice reinforcement drawings from me if you has a need wink

Re: Fortran DLL for VBA / VB6 programs

There are a few problems above that I can see, but nothing severe.  First, in the Fortran code, you might run into some issues with calling a DLL from Visual Basic related to the calling convention.  I would suggest changing two things in your Fortran code.  First, we need to instruct Fortran to use the STDCALL convention, which is the usual way DLL routines are called.  Second, we need to let Fortran know that it will be receiving values as inputs, not pointers as it might expect.  I would change your code to be:

function testme()
!GCC$ attributes dllexport, stdcall :: testme
    integer testme
    testme=999
end

function testi(i,j)
!GCC$ attributes dllexport, stdcall :: testi
integer, value :: i
integer, value :: j
integer testi       
    testi = j
end 

Notice the parameters to testi now have the "value" attribute.  Also, we're using a compiler directive to let the Fortran compiler know that these two functions should be available as STDCALL'd routines.

Next, you'll need to change your Visual Basic definitions of these functions.  We need to declare these input functions appropriately.  You've declared all inputs and outputs as Longs, but they are clearly Integers in this case.  Normally I suggest using the Fortran ISO_C_BINDING module, but we'll skip that for now and just change these types.  Also, we have to specify that these integers being passed into testi will be ByVal, an important distinction.  Finally, we need to create an alias that points to the proper functions.  Because we've switched to STDCALL conventions, the function names in the DLL will be appended with the number of parameters times 4.  Our calls become:

    Declare Function testme Lib "C:\Users\Jeffrey\Workspace\example\vb2dll\fortran-library.dll" Alias "testme@0" () As Integer
    Declare Function testi Lib "C:\Users\Jeffrey\Workspace\example\vb2dll\fortran-library.dll" Alias "testi@8" (ByVal i As Integer, ByVal j As Integer) As Integer

The above is a bit of a mess, and we could probably fix things by suppressing the STDCALL decorations if desired.  However, the above should get you started.

Jeff Armstrong
Approximatrix, LLC

Re: Fortran DLL for VBA / VB6 programs

Hi Jeff

Thanks yes that works for this simple example !

(even i would not come in a million years on such a construct - in the net you find just outdated BS and i used a lot of time to get something together)

Seems compiler parameter are in GCC a nice to have thing a logic behind i could not see nor even someone who was brave enough to describe them like as a human. No for sure instead the hint to some mailing lists ... (or stated just look in the asm code (civil - engineers and linux hackers are not compatible i guess)) In such cases i prefer a manual with a few examples which just work.

I played around a bit with your code:
the compiler directive -stdcall  -add-stdcall-alias seems useless if you want to put something as a parameter.
-stdcall  -add-stdcall-alias will remove the @(noone needs that really) from the exportet mangled names
but then you can not place parameters hmm

So yes you are right - i want to get rid this decorations. Imagine you are prototyping wants to put user defined types in a function - and i have a few in mind which have endless data structures - and first you has to either count bits or to use a tool like dependency walker etc just to look how their current name is ...


In the literature its BTW also pointed out that fortran puts its parameters BYREF or as a pointer.
For speed or large datastructures a good option.
Just as example some of my strings are very large - some megas sometimes and for each simple function call i pump them as a copy to the - wherever
Takes time. its also not recomanded by subroutines where you want to get them for sure changed back.

Just to avoid salami tactics - slice by sliece

Imagine you would have such a construct in VBA

Type POINT3D
id :LONG
x: double
y: double
z: double
INFO :string
active as BOOLEAN
end type

and you want to push that trough a fortran routine.
(as a array for example)

Sub challange_Jeff

dim P() as POINT3D
redim P(200000)

P(0).id=0
P(0).x=1
P(0).y=2
P(0).z=3
P(0).INFO="THANKS JEFF"

id=0
dim s as string
s= FORTRAN_DLL_return_me_info (id,"INFO")'should return "thanks jeff" id address the first element

call FORTRAN_REDIM_POINTS(P(),200) 'reduce size of the array to 200 but keep content intact - like redim preserve

s= FORTRAN_DLL_return_me_info (id,"INFO")'should return "thanks jeff" id address the first element


BTW it might help also to sell Simly fortran by providing some of such REAL LIFE examples.
I am quite sure a "few" peoples are could just be interesting in simply fortran if they can mix gfortran with excel.

A colegue of me it a bit older -  he never leared something else then fortran - he is a specialized engineer for anchors.
He is good in two thing Anchors and Fortran ...
I tried to make learn him basic - answer i have no time for that ....
(Besides work he sit in the german DIN and eurocode associations - so i belive that he do not lie )
He really just has no time for such things.

You could make him totally happy if you could show him how to use fortran in excel ...

And also i think my code was very clear - that the way it should maybe someday work.
As a engineer i really hate constructs like :
!GCC$ attributes dllexport, stdcall :: testi
in my opinion its just a hack


and sure you are totally right - that is a mess ! :

Declare Function testi Lib "C:\Users\Jeffrey\Workspace\example\vb2dll\fortran-library.dll" Alias "testi@8" (ByVal i As Integer, ByVal j As Integer) As Integer

Would work fine for example in freebasic ...

Declare Function testi Lib "C:\Users\Jeffrey\Workspace\example\vb2dll\fortran-library.dll" (i As Long, j As Long) As Long



Have a nice friendly day ! smile
Thomas

In private i gave linux a try for years - but in real buisiness - i mean not IT or not in a university
Its somehow useless except as a database / file server in the dungeon of the office basement but that's it hmm

Re: Fortran DLL for VBA / VB6 programs

Thomas,

I'm not sure what support Visual Basic provides for passing custom types around.  Fortran is, of course, completely compatible with C structures, so I'll need to look into seeing if VB and C structs are compatible.  If so, it should be rather trivial (using the ISO_C_BINDING intrinsic module in Fortran).  The only problem I can foresee in your example is the string member of your type, which tends to be problematic in Fortran.  Since Fortran handles strings differently than most other languages, passing them around can be somewhat messy.

I'll look at providing a better way to remove the decorations.  I'm not sure why you had problems with it, so I'll have to have a closer look.

Jeff Armstrong
Approximatrix, LLC

Re: Fortran DLL for VBA / VB6 programs

Hi Jeff
Thanks for the reply.

Yes in ther point 3D type each of the array points has a string
Useful in some cases if you need to add some quick and dirty informations in this geometric case.
But in real engineering world often types have a string - usually all
A example
a anchor plate has a length a height a thickness a number of studs - so far so good just numbers
stupidly such studs have a TYPE "Nelson headed stud"
a serial number "Plate 4711" and so on.
Its usually a good idea to keep those information together - otherwise you have to take care of some data structures. To keep them consolidated is "fun"
Its unhandy in most cases.

So back to string parameters
To ease your live i grabbed a few string routines together to move VBA strings in byte arrays as unicode or ansi and back.

I append it to the vba part
Yesterday i tested ho to deal with singles or real values.
So a VBA single is a FORTRANreal.
length is 8 bytes as i could see by dependancywalker.

I do not like this decorations cause
after i wrote and copiled the fortran code i have to look by dependany walker how the mangled decorations looks like...
Imagine you have a few hundred subroutines ...

Back to topic:

I guess fortran uses 8 bytes for the string
so i asume
1. th 4 bytes are for the length and
2. th 4 for the pointer of the first char - if web information is valid


FORTRAN CODE :

function testme() ! CRASHES VBA
!GCC$ attributes dllexport, stdcall :: testme
  character c
  character testme
  c="Hello World"
  testme=c
end

function testi(i,j)
! interface a vba integer by value
!GCC$ attributes dllexport, stdcall :: testi
integer, value :: i
integer, value :: j
integer testi       
    testi = j+i
end


function testr(a,b)
! interface a vba single by value     
!GCC$ attributes dllexport, stdcall :: testr

real, value :: a
real, value :: b
real testr       
    testr = 3.1415+b
end

VBA code


''Jeff
'Declare Function testme Lib "C:\Users\Jeffrey\Workspace\example\vb2dll\fortran-library.dll" Alias "testme@0" () As Integer
'Declare Function testi Lib "C:\Users\Jeffrey\Workspace\example\vb2dll\fortran-library.dll" Alias "testi@8" (ByVal i As Integer, ByVal j As Integer) As Integer

'Thomas
Declare Function testme Lib "M:\DEVEL\FORTRAN\orion.math.iastate.edu\burkardt\f_src\geometry\fortran-library.dll" Alias "testme@0" () As Integer
Declare Function testi Lib "M:\DEVEL\FORTRAN\orion.math.iastate.edu\burkardt\f_src\geometry\fortran-library.dll" Alias "testi@8" (ByVal i As Integer, ByVal j As Integer) As Integer
Declare Function testr Lib "M:\DEVEL\FORTRAN\orion.math.iastate.edu\burkardt\f_src\geometry\fortran-library.dll" Alias "testr@8" (ByVal a As Single, ByVal b As Single) As Single


'WIN API FUNCTIONS FOR CHARACTER CONVERSATIONS SPEEDTESTS AND MEM FUNCS

'CodePage
Private Const CP_ACP = 0    'ANSI
Private Const CP_MACCP = 2    'Mac
Private Const CP_OEMCP = 1    'OEM
Private Const CP_UTF7 = 65000
Private Const CP_UTF8 = 65001

'dwFlags
Private Const WC_NO_BEST_FIT_CHARS = &H400
Private Const WC_COMPOSITECHECK = &H200
Private Const WC_DISCARDNS = &H10
Private Const WC_SEPCHARS = &H20    'Default
Private Const WC_DEFAULTCHAR = &H40

Private Declare Function WideCharToMultiByte Lib "kernel32" (ByVal CodePage As Long, ByVal dwFlags As Long, ByVal lpWideCharStr As Long, ByVal cchWideChar As Long, ByVal lpMultiByteStr As Long, ByVal cbMultiByte As Long, ByVal lpDefaultChar As Long, ByVal lpUsedDefaultChar As Long) As Long
Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDst As Any, pSrc As Any, ByVal ByteLen As Long)
'The WideCharToMultiByte function maps a wide-character string to a new character string. 'The function is faster when both lpDefaultChar and lpUsedDefaultChar are NULL.

Sub fortran()

    Dim i As Integer
    Dim j As Integer
    Dim k As Integer

    Dim a As Single
    Dim b As Single

    ' k = testme() 'kills excel big_smile
    Debug.Print k

    i = 1
    j = 2
    a = 1
    b = 2
    Debug.Print testi(i, j)  'should return 2 - wrom dll calling conventoin error or crash
    Debug.Print testr(a, b)  'should return 5.1415
End Sub


'helper functions for string parameters.....


Public Sub unicode_string_to_bytearray()
    Dim b() As Byte
    Dim s As String
    s = "Whatever"
    b = s  'Assign Unicode string to bytes.'
    s = ""
    s = b  'Works in reverse, too!'
    Debug.Print s
    For i = 0 To UBound(b)
        Debug.Print b(i)
    Next
End Sub


Public Function Byte_Array_To_String(bytArray() As Byte) As String
    Dim s As String
    Dim i As Long
    s = StrConv(bytArray, vbUnicode)
    i = InStr(s, Chr(0))
    If i > 0 Then s = Left(s, i - 1)
    Byte_Array_To_String = s
End Function




Sub string_to_bytearray_2()
    Dim s As String
    Dim l As Long
    Dim b() As Byte
    s = "Good morning vietnam!"
    l = Len(s)
    b = StrConv(s, vbFromUnicode)
    Debug.Print l, UBound(b)
    Debug.Print Byte_Array_To_String(b())
End Sub



Private Function ByteArrayToString(Bytes() As Byte) As String
    Dim iUnicode As Long, i As Long, j As Long

    On Error Resume Next
    i = UBound(Bytes)

    If (i < 1) Then
        'ANSI, just convert to unicode and return
        ByteArrayToString = StrConv(Bytes, vbUnicode)
        Exit Function
    End If
    i = i + 1

    'Examine the first two bytes
    CopyMemory iUnicode, Bytes(0), 2

    If iUnicode = Bytes(0) Then    'Unicode
        'Account for terminating null
        If (i Mod 2) Then i = i - 1
        'Set up a buffer to recieve the string
        ByteArrayToString = String$(i / 2, 0)
        'Copy to string
        CopyMemory ByVal StrPtr(ByteArrayToString), Bytes(0), i
    Else    'ANSI
        ByteArrayToString = StrConv(Bytes, vbUnicode)
    End If

End Function



Private Function StringToByteArray(strInput As String, Optional bReturnAsUnicode As Boolean = True, Optional bAddNullTerminator As Boolean = False) As Byte()

    Dim lRet As Long
    Dim bytBuffer() As Byte
    Dim lLenB As Long
    Dim method As Long
    method = 2
    If bReturnAsUnicode Then
        'Number of bytes
        lLenB = LenB(strInput)
        'Resize buffer, do we want terminating null?
        If bAddNullTerminator Then
            ReDim bytBuffer(lLenB)
        Else
            ReDim bytBuffer(lLenB - 1)
        End If
        'Copy characters from string to byte array
        CopyMemory bytBuffer(0), ByVal StrPtr(strInput), lLenB
    Else
        If method = 1 Then
            'METHOD ONE
            'Get rid of embedded nulls
            strRet = StrConv(strInput, vbFromUnicode)
            lLenB = LenB(strRet)
            If bAddNullTerminator Then
                ReDim bytBuffer(lLenB)
            Else
                ReDim bytBuffer(lLenB - 1)
            End If
            CopyMemory bytBuffer(0), ByVal StrPtr(strInput), lLenB

        Else
            'METHOD TWO
            'Num of characters

            lLenB = Len(strInput)
            If bAddNullTerminator Then
                ReDim bytBuffer(lLenB)
            Else
                ReDim bytBuffer(lLenB - 1)
            End If
            lRet = WideCharToMultiByte(CP_ACP, 0&, ByVal StrPtr(strInput), -1, ByVal VarPtr(bytBuffer(0)), lLenB, 0&, 0&)
        End If
    End If

    StringToByteArray = bytBuffer

End Function



Private Sub s2b()
    Dim bAnsi() As Byte
    Dim bUni() As Byte
    Dim str As String
    Dim i As Long

    str = "Convert"
    bAnsi = StringToByteArray(str, False)
    bUni = StringToByteArray(str)

    For i = 0 To UBound(bAnsi)
        Debug.Print "=" & bAnsi(i)
    Next

    Debug.Print "------------------------------"

    For i = 0 To UBound(bUni)
        Debug.Print "=" & bUni(i)
    Next

    Debug.Print "ANSI= " & ByteArrayToString(bAnsi)
    Debug.Print "UNICODE= " & ByteArrayToString(bUni)
    'Using StrConv to convert a Unicode character array directly
    'will cause the resultant string to have extra embedded nulls
    'reason, StrConv does not know the difference between Unicode and ANSI
    Debug.Print "Result= " & StrConv(bUni, vbUnicode)
End Sub



Sub Test()
    Dim X As Long, T As Long, m As String

    Const sTESTc As String = "1234567890"

    T = GetTickCount
    For X = 1 To 100000
        Method1 sTESTc
    Next
    m = "It took Method1" + str$((GetTickCount - T) / 1000) + " secs. to run 10000 times." & vbCrLf

    T = GetTickCount
    For X = 1 To 100000
        Method2 sTESTc
    Next
    m = m & "It took Method2" + str$((GetTickCount - T) / 1000) + " secs. to run 10000 times." & vbCrLf

    T = GetTickCount
    For X = 1 To 100000
        Method3 sTESTc
    Next
    MsgBox m & "It took Method3" + str$((GetTickCount - T) / 1000) + " secs. to run 10000 times."

End Sub

Sub Method1(sVal As String)
    Dim lIndx As Long
    Dim byMyArray() As Byte
    byMyArray = VBA.StrConv(sVal, vbFromUnicode)
    For lIndx = 0 To UBound(byMyArray)
        byMyArray(lIndx) = CByte(VBA.Chr$(byMyArray(lIndx)))
    Next lIndx
End Sub

Sub Method2(sVal As String) 'fastest of those three methods
    Dim lIndx As Long
    Dim byMyArray() As Byte
    Dim lLen As Long
    lLen = VBA.Len(sVal)
    ReDim byMyArray(1 To lLen) As Byte
    For lIndx = 1 To lLen
        byMyArray(lIndx) = CByte(VBA.Mid$(sVal, lIndx, 1))
    Next lIndx
End Sub

Private Sub Method3(sVal As String)
    Dim s As Byte, Cnt As Long, Length As Long, byMyArray() As Byte

    Length = Len(sVal)
    ReDim byMyArray(1 To Length)

    For Cnt = 0 To Length - 1
        s = CByte(Mid$(sVal, Cnt + 1, 1))
        CopyMemory ByVal VarPtr(byMyArray(1)) + Cnt, ByVal VarPtr(s), LenB(s)
    Next Cnt

End Sub

Have a nice day
Thomas

Re: Fortran DLL for VBA / VB6 programs

Thomas,

You're going to run into problems the way you've tried to handle strings.  There's a few things you need to look at:

First, you should have a look at the Fortran ISO_C_BINDING module.  It is documented in Simply Fortran's help file under "Intrinsic Modules" in the Compiler Reference.  This eliminates all confusion surrounding what size a single in VBA translates to in Fortran.

Second, strings in C are essentially byte arrays ending in a null character.  Modern VBA most likely uses Unicode, which is not necessarily compatible with Fortran.  You would have to first convert the string to ANSI and then pass the resulting bytes into Fortran. 

On the Fortran end, the string will appear as a TYPE(C_PTR) (defined in ISO_C_BINDING)when passed in from C.  I'm guessing that the properly translated string from VBA, as described above, will be completely compatible with C strings.  You'll need to construct a Fortran string from the TYPE(C_PTR) input, which isn't particularly hard.  When building the string in Fortran, you can usually just pass a string back to C provided that you append the C_NULL_CHAR to the end of the string. 

I was more implying in my last reply that I wasn't sure how VBA deals with strings and C-compatible structures.  I'll try to construct a working example for you tomorrow and post the results here.

Jeff Armstrong
Approximatrix, LLC

Re: Fortran DLL for VBA / VB6 programs

Hi Jeff smile
if i do not know enough about how fortran (C) works really inside.
So i first look what i can provide. So this routines are just a collection whats easy possible with vba. Its even hard usually to get informations to convert strings to byte arrays and especially by the fact that vba and vb6 trie to handle strings INTERNALLY (user view) how they was handled in dos.  Visualbasic was in fact just a successor from q(gw)basic (8bit) - just with a "graphical" (semi graphic by characters) user interface. Microsoft did a not so bad job to keep compatibility. Later on comes unicode and somehow they was not able to provide simple conversation routines. There is no ANSITOUNICODE VBATOCSTRING routine. Thats why i post those routines there. If fortran for example awaits a byte array of chars no problem at least we can provide that. So if i have a bytearray i could use the not documented addressof function to get a pointer to this array. Now we just gave to find a function for then length (4 byte ?) information. Is it stored in front or at the end ?.
with this if we know how fortran uses strings we can prepare a byte array ubicode ansi whatever and put  the lengt info.
This is in vba done usually as far i understand winapi constructs by the vba declare statement.

Lets assume we have a correct string as byte array...
declare function call_fortran_string lib "blah" usw (a() as byte ,l as integer)
or
declare function call_fortran_string lib "blah" usw (l as integer, a() as byte )

---------------------- a bit polemic smile --------------------

I read the ISO_C_BINDING section of the manual - but sorry i am too stupid to get vulnerable information of it.
Maybe i first should learn C ASM try to build a few compiler and a operating system ...
Might then be easy *1
The provided information might be nice for C programmers but they are not useful for people who have no meaning about C.
Why we should use fortran basic and whatever if C would be so "easy" - no need for !.

*laugh* call us lazy stupid bastards - but it seems that IT guys just see IT (logically)
- if i would earn my bread just with thinking about how to get a pointer to a string trough a compiler - no problem.

Imagine you has also to deal with construction supervisors, making drawings, do calculations.

1*) OK a bit unfair *grin* Imagine someones put 8 meters of book on your table and said here is all the information to build a bridge - for sure you has to maintain your computers but i need a bridge
Usually the next question is -  WHEN its ready ???
------------------- polemic off ------------------------------


So back to topic . i have no idea how i have to construct a gfortran string hmm
Nor how to cheat the compiler to accept it.
And for sure to get it as a result back is also "challanging" (if you do not know how is the convention)

Its a bit like me and my girlfriend.
1. callange i am a male she is a female.
Ok usually enough to have fun by different thinking.
To make it a bit more special - she speaks bative suomi (finnish) and english.
I speak native german and english...
I am quite sure you will say that english is the most beautiful language in the world smile
She will say - to long
I will say - not exact enough
(Suomi sounds BTW like klingon)
There was no way to extend my english - and alo no way to extend her german
At least not in time - so we use a interface called english ...

Anyhow i am glad if you could put something together which will work smile
Or give a glue how it could work, No need for hurry. Ive to work in a few hours on the construction site and after 60 hours i will sit in a finnish sauna with a cold Karhu beer smile  I hope we read on Monday
Best regards from Finnland
Thomas smile

and for sure thanks for your support !

Re: Fortran DLL for VBA / VB6 programs

Thomas,

I have a nice example for you.  First, the VBA code:

Module Module1

    Structure Passed
        Dim ix As Integer
        Dim ry As Double
        Dim buffer As String
    End Structure

    Declare Sub handle_type Lib "types.dll" Alias "handle_type@4" (ByRef arg As Passed)

    Sub Main()

        Dim arg As Passed
        Dim tbuffer As String

        arg.ix = 2
        arg.ry = 1.5
        arg.buffer = "Me"

        Call handle_type(arg)

        Console.WriteLine("Integer is now:" + Str(arg.ix))
        Console.WriteLine("Real is now:" + Str(arg.ry))

        Console.WriteLine("Fortran says: '" + arg.buffer + "'")
        Console.WriteLine("press [ENTER]...")
        Console.ReadLine()

    End Sub

End Module

In the above example, you'll see that I'm still creating an alias.  I still don't have a particularly good fix for that yet, but stay tuned...

I'm passing a type that includes an integer, a real, and a string. The Visual Basic code isn't particularly interesting.  Now, on to the Fortran code:

module typedefs
    use iso_c_binding
    implicit none
    
    integer, parameter::bufsize = 40
    
    type, bind(c) :: passed
        integer(kind=c_int)::ix
        real(kind=c_double)::ry
        type(c_ptr)::buffer
    end type passed
    
    contains
    
    subroutine to_array(string, array)
    implicit none
    integer::i
    character(*)::string
    character, dimension(:)::array
        
        do i=1,len_trim(string)
            array(i) = string(i:i)
        end do
        array(i) = C_NULL_CHAR
        
    end subroutine to_array

end module typedefs

subroutine handle_type(io) bind(c)
use iso_c_binding, only: c_null_char
use typedefs
implicit none
!GCC$ attributes dllexport, stdcall :: handle_type

type(passed), intent(inout)::io

character, dimension(:), pointer::local

    io%ix = 5*io%ix
    
    call C_F_POINTER(io%buffer, local, (/40/))

    ! Set some new contents
    call to_array("From Fortran!", local)

end subroutine handle_type

In this example, I'm exercising some components of Fortran 2003 standards.  First, I have a module where I declare my type.  Note that the type has BIND(C) in its declaration.  This is necessary because we need Fortran to build a C-compatible structure (which is what VBA's "Structure" declaration is also doing).  Also, rather than a character array, I've defined the component "buffer" as TYPE(C_PTR).  This says that the structure component is a pointer as defined by C.

To work with the string pointed to by buffer, we need to assign a Fortran character array to it.  I'm using the intrinsic procedure C_F_POINTER that assigns a C pointer to a valid Fortran pointer (which also needs type information, unlike C). 

Finally, when I want to store a string into the array,  I need to convert the Fortran string to a Fortran character array, a subtle distinction, using the included to_array subroutine. 

When I execute the VB code, I get the expected "From Fortran!" message on the console.

Hopefully some of the above makes sense.  In order to properly perform mixed-language programming, things can get pretty complicated, and you really need to use parts of the Fortran 2003 standard to reliably achieve the desired results.

Jeff Armstrong
Approximatrix, LLC