Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Complex string inter language passing

van_der_merwe__ben
New Contributor I
339 Views

Hi,

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.

0 Kudos
2 Replies
IanH
Honored Contributor II
339 Views

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

 

 

0 Kudos
van_der_merwe__ben
New Contributor I
339 Views

Thank you IanH, I am going with your option 1, and it seems to work.

0 Kudos
Reply