Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
Announcements
The Intel sign-in experience is changing in February to support enhanced security controls. If you sign in, click here for more information.

x64 string passing

HarryWelten
Beginner
698 Views

Hi,

I have a VB.Net application calling Fortran dll's and I needed to migrate to x64. I use VS2019 and IVF V19. It crashes in the Fortran call where I need a returned string. I changed the string length parameter to a Long in following sample. Also tried ISO binding, but it crashes when entering the DLL. Sample VB.Net source frmTis.vb:

Declare Sub CMPNMS Lib "TISdlld_x64.dll" (ByVal IND As Integer, ByRef str As String, ByVal lang As Long)

Dim i As Integer
Dim BigLen As Long
Dim cmpnam as String
i = 1
cmpnam = "------"
BigLen = Len(cmpnam)
Call CMPNMS(i, cmpnam, BigLen) ' Call Fortran procedure cmpnam to return string 1, but crashes.

Sample Fortran file Tis.for:

subroutine CMPNMS (COUNT, NAME, LNG) bind(C)
!DEC$ ATTRIBUTES DLLEXPORT::CMPNMS
use, intrinsic :: ISO_C_BINDING
character(1,C_CHAR),target ::NAME(*)
integer(C_INT32_T),value::COUNT ! 4-byte integer
integer(C_SIZE_T),value::LNG ! 8-byte Long
character(LNG,C_CHAR), pointer :: fptr
type(C_PTR) cptr
integer*8 LNG

cptr = C_LOC(NAME(1))
call C_F_POINTER(cptr,fptr)
print *, fptr ! content of string of caller
NAME = 'NewStr' ! return a new string; always 6 char
LNG = LEN(NAME)
return

The Fortran calling convention was in x86 STDCALL, now default
String Length Argument passing is After Individual String Argument.

It gives runtime AccessViolationException at CMPNMS(Int32 IND, String& str, Int64 lang)
Any help is appreciated. Kind regards, Harry.

0 Kudos
1 Solution
FortranFan
Honored Contributor II
636 Views

@HarryWelten ,

Immediately I am unable to spend the time to read through your code and point you to any changes.  But I had this simple example for a colleague who previously had a similar question.  Perhaps you can try out this simple case and see if you can reproduce what is shown below  If yes, that might point you as to how to proceed with your code?

Fortran "library" code:

 

module Fdll_m

   use, intrinsic :: iso_c_binding, only : c_char, c_size_t, c_loc, c_f_pointer

contains

   subroutine Fstr(str, lenstr) bind(C, name="Fstr")
   !DEC$ ATTRIBUTES DLLEXPORT::Fstr

      ! Argument list
      character(kind=c_char,len=1), intent(in), target :: str(*)
      integer(c_size_t), intent(in), value             :: lenstr

      ! Elided are any checks for the lenstr value

      block

         character(kind=c_char,len=lenstr), pointer :: pstr

         call c_f_pointer( cptr=c_loc(str), fptr=pstr )
         
         pstr = c_char_"Hello World!"
         pstr => null()
         
      end block

      return

   end subroutine

end module

 

 

Visual Basic calling program in .NET:

 

Imports System.IO
Imports System.Text
Imports System.Runtime.InteropServices

Namespace Fortran

   Public Class Fdll

      <DllImport("Fdll.dll", CallingConvention:=CallingConvention.Cdecl)> _
      Public Shared Sub Fstr (A As StringBuilder, lenA As UIntPtr)
      end Sub

   End Class

   NotInheritable Class Test

      Private Sub New()
      End Sub

      Public Shared Sub Main()

         Dim s As StringBuilder

         ' Write header
         Console.WriteLine("*** Test Fortran Interop using VB ***" + vbLf)

         Try

            s = New StringBuilder(12)
            Fdll.Fstr(s, s.Capacity)

            Console.WriteLine("s = " + s.ToString())

         Catch ex As Exception

            Console.WriteLine(ex.Message)

         Finally

            Console.WriteLine("Press any key to continue..")
            Console.ReadKey()

         End Try

      End Sub

   End Class

End Namespace

 

 

Program execution using Intel Fortran:

 

C:\Temp>ifort /dll /libs:static Fdll.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.3.0 Build 20210609_000000
Copyright (C) 1985-2021 Intel Corporation.  All rights reserved.

Microsoft (R) Incremental Linker Version 14.29.30038.1
Copyright (C) Microsoft Corporation.  All rights reserved.

-out:Fdll.dll
-dll
-implib:Fdll.lib
Fdll.obj
   Creating library Fdll.lib and object Fdll.exp

C:\Temp>vbc -platform:x64 test.vb
Microsoft (R) Visual Basic Compiler version 3.10.0-4.21318.11 (7ceb6331)
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Temp>test.exe
*** Test Fortran Interop using VB ***

s = Hello World!
Press any key to continue..

 

 

 

View solution in original post

9 Replies
FortranFan
Honored Contributor II
637 Views

@HarryWelten ,

Immediately I am unable to spend the time to read through your code and point you to any changes.  But I had this simple example for a colleague who previously had a similar question.  Perhaps you can try out this simple case and see if you can reproduce what is shown below  If yes, that might point you as to how to proceed with your code?

Fortran "library" code:

 

module Fdll_m

   use, intrinsic :: iso_c_binding, only : c_char, c_size_t, c_loc, c_f_pointer

contains

   subroutine Fstr(str, lenstr) bind(C, name="Fstr")
   !DEC$ ATTRIBUTES DLLEXPORT::Fstr

      ! Argument list
      character(kind=c_char,len=1), intent(in), target :: str(*)
      integer(c_size_t), intent(in), value             :: lenstr

      ! Elided are any checks for the lenstr value

      block

         character(kind=c_char,len=lenstr), pointer :: pstr

         call c_f_pointer( cptr=c_loc(str), fptr=pstr )
         
         pstr = c_char_"Hello World!"
         pstr => null()
         
      end block

      return

   end subroutine

end module

 

 

Visual Basic calling program in .NET:

 

Imports System.IO
Imports System.Text
Imports System.Runtime.InteropServices

Namespace Fortran

   Public Class Fdll

      <DllImport("Fdll.dll", CallingConvention:=CallingConvention.Cdecl)> _
      Public Shared Sub Fstr (A As StringBuilder, lenA As UIntPtr)
      end Sub

   End Class

   NotInheritable Class Test

      Private Sub New()
      End Sub

      Public Shared Sub Main()

         Dim s As StringBuilder

         ' Write header
         Console.WriteLine("*** Test Fortran Interop using VB ***" + vbLf)

         Try

            s = New StringBuilder(12)
            Fdll.Fstr(s, s.Capacity)

            Console.WriteLine("s = " + s.ToString())

         Catch ex As Exception

            Console.WriteLine(ex.Message)

         Finally

            Console.WriteLine("Press any key to continue..")
            Console.ReadKey()

         End Try

      End Sub

   End Class

End Namespace

 

 

Program execution using Intel Fortran:

 

C:\Temp>ifort /dll /libs:static Fdll.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on Intel(R) 64, Version 2021.3.0 Build 20210609_000000
Copyright (C) 1985-2021 Intel Corporation.  All rights reserved.

Microsoft (R) Incremental Linker Version 14.29.30038.1
Copyright (C) Microsoft Corporation.  All rights reserved.

-out:Fdll.dll
-dll
-implib:Fdll.lib
Fdll.obj
   Creating library Fdll.lib and object Fdll.exp

C:\Temp>vbc -platform:x64 test.vb
Microsoft (R) Visual Basic Compiler version 3.10.0-4.21318.11 (7ceb6331)
Copyright (C) Microsoft Corporation. All rights reserved.


C:\Temp>test.exe
*** Test Fortran Interop using VB ***

s = Hello World!
Press any key to continue..

 

 

 

HarryWelten
Beginner
579 Views

Thanks so much for your reply FortranFan. I'm in the process of converting the include files (14) and sources to Fortan-90. The compiler does not seem to like the line:   integer(C_INT32_T),value:: COUNT ! 4-byte integer.  It complains that the parameter must be a compile-time constant. I will keep you up-to-date with the result.

HarryWelten
Beginner
578 Views

It appears that the constant is satisfied by extending the USE clause.

HarryWelten
Beginner
411 Views

Thanks a lot FortranFan and JimDempseyAtTheCove for the solution to the problem. I cannot emphasize enough how valuable your replies are.

FortranFan
Honored Contributor II
360 Views

@HarryWelten , glad you were able to solve your problem.

A question if you are willing to share your feedback: do you plan to do further work on the Fortran side and if so, will you be considering modern Fortran (starting with free-form source and so much more)?

Or is it the case for you of needing to do continue with .for files as in "Tis.for" (that are usually FORTRAN 77 style fixed form source) and doing minimal to no changes in some existing codebase?

 By the way, I had reused the Fortran code I had posted in this thread toward another recent thread that involved part of a namesake i.e., Visual Basic for Applications as opposed to Visual Basic .NET though there are major differences between the two platforms.  It appears that user too was able to solve the problem, so for me it is good to see Fortran "libraries" remaining in use with different solutions, especially on Windows:

https://community.intel.com/t5/Intel-Fortran-Compiler/Help-with-passing-VBA-string-to-Fortran/m-p/13...

HarryWelten
Beginner
309 Views

Hi FortranFan,

I need to continue with the .for files because there is minimal time to upgrade this; the objective of the current project is to migrate to the 64-bit platform only. Thanks again!

HarryWelten
Beginner
470 Views

Hi,

I built this example that you gave, modified it slightly, run the x64 debug mode and that also crashes entering into the Fortran routine.

The VB part:

<DllImport("TISdlld_x64.dll", CallingConvention:=CallingConvention.Cdecl)>
Public Sub CMPNMS(ByVal IND As Integer, ByRef str As String, ByVal lang As Long)
End Sub

Dim i As Integer
Dim BigLen As Long
Dim cmpnam as String
i = 1
cmpnam = "------"
BigLen = Len(cmpnam)
Call CMPNMS(i, cmpnam, BigLen) ' Call Fortran procedure cmpnam to return string 1, but crashes.

Sample Fortran file Tis.for:

subroutine CMPNMS (COUNT, NAME, LNG) bind(C)
!DEC$ ATTRIBUTES DLLEXPORT::CMPNMS
use, intrinsic :: ISO_C_BINDING
character(1,C_CHAR),target ::NAME(*)
integer(C_INT32_T),value::COUNT ! 4-byte integer
integer(C_SIZE_T),value::LNG ! 8-byte Long
character(LNG,C_CHAR), pointer :: fptr
type(C_PTR) cptr
integer*8 LNG

cptr = C_LOC(NAME(1))
call C_F_POINTER(cptr,fptr)
print *, fptr ! content of string of caller
NAME = 'NewStr' ! return a new string; always 6 char
LNG = LEN(NAME)
return

jimdempseyatthecove
Black Belt
468 Views

Look at FortranFan's post on 4/1. His VB argument specification for the string was to pass

 

     A As StringBuilder

 

Yours is passing

 

    ByRef str As String

 

"String" is a container (containing several member variables one of which is the base address of the buffer for the string) whereas StringBuilder returns what amounts to the address of a null terminated string (IOW the string itself as opposed to the address of container).

 

Jim Dempsey

 

HarryWelten
Beginner
453 Views

Jim, thanks for your remark. I assumed it was a detail, but assumption is the mother of all f*. I will try it soon. Regards, Harry. 

Reply