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

cs string ToChar_Array pointer in fortran OpenFileMapping

MWind2
New Contributor III
1,970 Views

How to declare or transform cstr to use in OpenFileMapping? Previously I had tested what ToCharArray result got passed to fortran as cstr and all was correct including trailing 0.

 

string smf = "testf";
char[] acmf = smf.ToCharArray();
char[] acmf0 = new char[smf.Length + 1];
for (int i=0;i<smf.Length;i++) acmf0[i] = acmf[i];
acmf0[smf.Length] = (char)0;
int iz = smf.Length +1;
int iy = test2( ref acmf0, ref iz)
integer function test2(cstr, ilen)
!DEC$ ATTRIBUTES DLLEXPORT, alias : "test2" :: test2
use,INTRINSIC :: ISO_C_BINDING
use kernel32
type(c_ptr), intent(in) :: cstr
!...
hmmf=OpenFileMapping(dwDesiredAccess,FALSE,cstr)

produces
error #6633: The type of the actual argument differs from the type of the dummy argument. [CSTR]

From user32.f90

INTERFACE 
FUNCTION OpenFileMapping( &
        dwDesiredAccess, &
        bInheritHandle, &
        lpName)
import
  integer(HANDLE) :: OpenFileMapping ! HANDLE
    !DEC$ ATTRIBUTES DEFAULT, STDCALL, DECORATE, ALIAS:'OpenFileMappingA' :: OpenFileMapping
  integer(DWORD) dwDesiredAccess ! DWORD dwDesiredAccess
  integer(BOOL) bInheritHandle ! BOOL bInheritHandle
!DEC$ ATTRIBUTES REFERENCE, IGNORE_LOC, ALLOW_NULL :: lpName
  character*(*) lpName ! LPCSTR lpName
 END FUNCTION
END INTERFACE

 

 

0 Kudos
1 Solution
jimdempseyatthecove
Honored Contributor III
1,940 Views

Try:

integer function test2(cstr, ilen)
!DEC$ ATTRIBUTES DLLEXPORT, alias : "test2" :: test2
use,INTRINSIC :: ISO_C_BINDING
use kernel32
character(len=ilen), intent(in) :: cstr
!...
hmmf=OpenFileMapping(dwDesiredAccess,FALSE,cstr)

Jim Dempsey

 

View solution in original post

12 Replies
jimdempseyatthecove
Honored Contributor III
1,941 Views

Try:

integer function test2(cstr, ilen)
!DEC$ ATTRIBUTES DLLEXPORT, alias : "test2" :: test2
use,INTRINSIC :: ISO_C_BINDING
use kernel32
character(len=ilen), intent(in) :: cstr
!...
hmmf=OpenFileMapping(dwDesiredAccess,FALSE,cstr)

Jim Dempsey

 

MWind2
New Contributor III
1,931 Views

Works!

static class Program
{
[DllImport("D:\\c\\vs2022\\mmff\\fDll1\\x64\\Debug\\fdll1.dll")]
static extern unsafe int test3( char[] aC, ref int ilength);
    static unsafe void Main(string[] args)
{
System.IO.MemoryMappedFiles.MemoryMappedFile mmf = System.IO.MemoryMappedFiles.MemoryMappedFile.CreateNew("testf", 42);
string smf = "testf";
char[] acmf = smf.ToCharArray();
char[] acmf0 = new char[smf.Length + 1];
for (int i=0;i<smf.Length;i++)
acmf0[i] = acmf[i];
acmf0[smf.Length] = (char)0;
int iz = smf.Length +1;
int iy = test3( acmf0, ref iz);
}
}
integer function test3(lpname, ilen)
!DEC$ ATTRIBUTES DLLEXPORT, alias : "test3" :: test3
use,INTRINSIC :: ISO_C_BINDING
use user32
use kernel32
!use ifwin
character(len=ilen), intent(in) :: lpname
integer, intent(in) :: ilen
!
integer :: ilen_max = 512
integer :: i
integer :: dwDesiredAccess = Z'0f001f'
integer(8) :: xptr=0
integer(8) :: hmmf=0
integer :: iunmap=0
integer :: ihclose=0
!
hmmf=OpenFileMapping(dwDesiredAccess,FALSE,lpname)
if (hmmf == 0) then
   test3=-1
   return
else
  xptr = MapViewOfFile(hmmf,dwDesiredAccess,0,0,42) 
  iunmap = UnmapViewOfFile(xptr)
  ihclose = CloseHandle(hmmf)
end if   
test3=ilen
return
end function test3
0 Kudos
MWind2
New Contributor III
1,923 Views

Another question about int(8) ::xptr. It was the only thing I found that would not give error in MapViewOfFile, but now I find no way to convert to cptr with the intent of reading/writing to  the array. I had hoped to convert to fptr array (0:ilen).

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,918 Views

The return of MapViewOfFile is of (C) type LPVOID. While on 64-bit build this would be handled with integer(8), on a 32-bit build this would be incorrect. 

If instead, as you have USE KERNEL32 in your procedure, you can then use the "Windows" types that are defined therein. Example

integer(LPVOID)

 

*** In your code above, you have initialization of the variables when they are declared.

There are two aspects to this in Fortran that are different from C/C++ and might bight you in the ...

1) variables that are initialized at declaration (Fortran speak is defined at....) have attribute SAVE (C analog to static). This can make your procedure .NOT. thread-safe.

2) Initialization is made in the load/binary image and not at execution time. IOW these will not be re-initialized at every call.

 

I suggest you not, initialize at declaration (unless you absolutely know this procedure will be called only once). IOW if you foresee mapping multiple files or different sections of the same file you may experience the programming error.

 

Jim Dempsey

0 Kudos
MWind2
New Contributor III
1,909 Views

Just before your reply I found the integer(LPVOID)  and started trying to use unsuccessfully in the sense that I'm trying make a char(1) array that I can read/write that would alter memory mapped file. The points 1 and 2 you make I will have to remember and take care of in the code once I can get the basic array to work. 

0 Kudos
MWind2
New Contributor III
1,903 Views

I guess I should ask "Is there a way to create a integer(1) array from a non null integer(LPVOID)?"

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,899 Views

xptr = MapViewOfFile(hmmf,dwDesiredAccess,0,0,42)

the first 0 is the high 32-bit offset in the file (don't know why MS has this "backwards")

the second 0 is the low 32-bit offset in the file

and the 42 is the size of the object

For use in Fortran, you would likely want to use the returned address as one of:

 

character(len=42) :: charptr

character(len=1) :: chararray(42)

integer(1) :: int1array(42)

 

The 42, in your code would likely be replaced with a variable (to facillitate returning different object types/sizes)

You may want to create a function that returns one of:

type(yourObject), pointer :: ret

character(len=yourLen), pointer :: ret 

character(len=1), pointer :: ret(youLen)

integer(1), pointer :: ret(youLen)

 

or NULLIFY the return pointer in event of error.

 

You would make use of the C_F_POINTER intrinsic to convert the LPVOID (if it complains, you may need to convert the LPVOID into C_PTR first)

 

 

function test3(lpname, ilen) result(ret)
!DEC$ ATTRIBUTES DLLEXPORT, alias : "test3" :: test3
use,INTRINSIC :: ISO_C_BINDING
use user32
use kernel32
!use ifwin
character(len=ilen), intent(in) :: lpname
integer, intent(in) :: ilen
type yourObjectType
 integer(1) :: blob(42)
end type yourObjectType
type(yourObjectType), pointer :: ret
!
integer, parameter :: ilen_max = 512
integer :: i
integer, parameter :: dwDesiredAccess = Z'0f001f'
integer(8) :: xptr
integer(8) :: hmmf
integer :: iunmap
integer :: ihclose
type(c_ptr) :: cptr
!
ilen_max = 512
dwDesiredAccess = Z'0f001f'
xptr=0
hmmf=0
iunmap=0
ihclose=0

hmmf=OpenFileMapping(dwDesiredAccess,FALSE,lpname)
if (hmmf == 0) then
   nullify(ret)
   return
else
  xptr = MapViewOfFile(hmmf,dwDesiredAccess,0,0,sizeof(ret))
  call C_F_POINTER(transfer(xptr,cptr), ret)
! here is where you either return ret or use ret
! the unmap and close would occur on other call
! the hmmf and xptr would have to be saved somewhere
! if returning object pointer
  iunmap = UnmapViewOfFile(xptr)
  ihclose = CloseHandle(hmmf)
end if   
return
end function test3

 

*** untested code

 

Jim Dempsey

0 Kudos
MWind2
New Contributor III
1,887 Views

This works in a primitive way:

static class Program
    {
        [DllImport("D:\\c\\vs2022\\mmff\\fDll1\\x64\\Debug\\fdll1.dll")]
        static extern unsafe int test3(  char[] aC, ref int ilength, ref int immflen);
        static unsafe void Main(string[] args)

        {
            int immf_len = 42;
            byte* bpoke = null;
            System.IO.MemoryMappedFiles.MemoryMappedFile mmf = System.IO.MemoryMappedFiles.MemoryMappedFile.CreateNew("testf", immf_len);
            System.IO.MemoryMappedFiles.MemoryMappedViewAccessor view = mmf.CreateViewAccessor();
            view.SafeMemoryMappedViewHandle.AcquirePointer(ref bpoke);
            string smmf = "abcdefghijklmnopqrstuvwxyz0123456789<>^!=;";
            if (smmf.Length > immf_len) smmf = smmf.Substring(0, immf_len);
            char[] acmmf = smmf.ToCharArray();
            for (int i = 0; i < 42; i++) *(bpoke + i) = (byte)acmmf[i];
            string smf = "testf";
            char[] acmf = smf.ToCharArray();
            char[] acmf0 = new char[smf.Length + 1];
            for (int i=0;i<smf.Length;i++)
                acmf0[i] = acmf[i];
            acmf0[smf.Length] = (char)0;
            int iz = smf.Length +1;
            int iy = test3(  acmf0, ref iz, ref immf_len);
        }
}
!  fdll1.f90 
!
!  FUNCTIONS/SUBROUTINES exported from fdll1.dll:
!  fdll2 - subroutine 
!
 
 !   implicit none
 !module fmdll1
 !
 !integer(8) :: hMapFile=0
 !integer(8) :: hmmf=0
 !
 !end module fmdll1
 ! 
integer function test3(lpname, ilen, immflen)
!DEC$ ATTRIBUTES DLLEXPORT, alias : "test3" :: test3
use,INTRINSIC :: ISO_C_BINDING
USE, INTRINSIC :: iso_fortran_env
use user32
use kernel32
character(len=ilen), intent(in) :: lpname
integer, intent(in) :: ilen
integer, intent (in):: immflen
!
integer(1), pointer :: fptr(:)
type(c_ptr) :: cptr
type(c_ptr) :: cdata
integer :: ilen_max = 512
integer :: i
integer :: dwDesiredAccess = Z'0f001f'
integer(LPVOID) ::cint=0
integer(8) :: hmmf=0
integer :: iunmap=0
integer :: ihclose=0
!
hmmf=OpenFileMapping(dwDesiredAccess,FALSE,lpname)
if (hmmf == 0) then
   test3=-1
   return
else
  cint = MapViewOfFile(hmmf,dwDesiredAccess,0,0,immflen)
  cdata=transfer(cint,cptr)
  call c_f_pointer(cdata,fptr,[immflen])
  fptr(4)='D'
  !
  open(1001, file = 'mmf.dat',  status = 'REPLACE', access='STREAM')
  write(1001),fptr
  close(1001)
  !
  iunmap = UnmapViewOfFile(cint)
  ihclose = CloseHandle(hmmf)
end if   
test3=ilen
return
end function test3

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,870 Views

Glad that you at least have a starting point. I assume you will rework test3 into a series of get/put functions.

 

Jim Dempsey

0 Kudos
MWind2
New Contributor III
1,862 Views

Thanks, there are many worms in this can like volatile, exchange, spin waits and locks equivalence.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,861 Views

>>volatile, exchange, spin waits and locks

Then this indicates that the code is currently threaded (but not necessarily via OpenMP). Is this the case?

 

Jim Dempsey

0 Kudos
MWind2
New Contributor III
1,860 Views

I did this once in c++to do multiprocess  and multithreading and it seemed like an interesting adventure.

0 Kudos
Reply