I am really hoping someone can help me with this (hopes Lionel's ears feel warm). We have this VBA code and cannot change it:
Private Declare Sub Func1 "Dll1.dll" _ ByVal TEXT1 As String, _ ByVal intTEXT1 As Long) Dim TEXT1 As String * 49152
TEXT1 gets set to "string1 string2 string3 and so on" before calling:
Call FUNC1(TEXT1, Len(TEXT1))
The Fortran code being called (originally Compaq) is:
!DEC$ ATTRIBUTES STDCALL,REFERENCE :: FUNC1 SUBROUTINE FUNC1(TEXT1) !DEC$ ATTRIBUTES DLLEXPORT :: FUNC1 !DEC$ ATTRIBUTES ALIAS: 'FUNC1' :: FUNC1 CHARACTER*(32) TEXT1(128, 12)
The problem is that even though we are compiling with the mixed string length argument option, Intel Fortran does not expect the string length and the VBA code crashes. The code half works if I do not pass the length from VBA (but keep in mind we cant change the VBA code permanently). Presumably Intel Fortran feels that the size of TEXT1 is fixed so it is not expecting a length argument (even though Compaq did).
So one idea is to to change the Fortran code as follows:
!DEC$ ATTRIBUTES STDCALL,REFERENCE :: FUNC1 SUBROUTINE FUNC1 (TEXT1B) CHARACTER*(*) TEXT1B CHARACTER*(32) TEXT1(128, 12) TEXT1 = TEXT1B
And that works, Intel Fortran now expects a string length to be passed and the VBA code is happy and not crashing. Except that TEXT1 ends up being an array containing "string1", "string1", "string1" etc. instead of "string1", "string2", "string3" etc.
Any ideas on how to best fix this? We can change the Fortran code any way we like, but we cant change the VBA code.
Please and thank you.
In your latter example, TEXT1B is a scalar character variable of some length (presumably 49152). Your assignment statement `TEXT1 = TEXT1B` then assigns that scalar to each element of the TEXT1 array, using the rules of Fortran character assignment (and Fortran character assignment just truncates if the right hand side (length 49152) is longer than the left hand side (length 32)) hence you see the repeated `string1` value in each array element.
With your latter approach, what you actually need to do is walk the length of the incoming long scalar string, and slice out each 32 character substring into its respective element. Something like:
integer :: i, j, start character(32) :: text1(128,12) ... start = 0 do j = 1, size(text1, 2) do i = 1, size(text1, 1) text1(i,j) = text1b(start+1:start+len(text1)) start = start + len(text1) end do end do
Alternatively, you could use some of the C interoperability stuff to associate a pointer with the string somewhat directly.
SUBROUTINE FUNC1(text_ptr, length) BIND(C, NAME='func1') USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER !DEC$ ATTRIBUTES STDCALL :: FUNC1 !DEC$ ATTRIBUTES DLLEXPORT :: FUNC1 TYPE(C_PTR), INTENT(IN), VALUE :: text_ptr INTEGER, INTENT(IN), VALUE :: length CHARACTER(LEN=32), POINTER :: text(:, :) CALL C_F_POINTER(text_ptr, text, [128, 12]) ! onward and upward with text... END SUBROUTINE FUNC1