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

Does Fortran support .net clr arrays?

MWind2
New Contributor III
570 Views

I was working on a c# memory mapped file  and view initializer for access by a fortran dll when I had passed from C#. I did not even know C# had unsafe as a option, so I thought ToCharArray was  native without checking. As the debugger in C# would not expand the pointer to mmf to show an array as in 

byte* pmmf = null;
            mmfvw.SafeMemoryMappedViewHandle.AcquirePointer( ref pmmf);

I decided to write c# code in cpp with clr(which expands unsigned char * pmmf in debugger with pmmf,256). Since C# string=CPP/System::String, I had to correct, realized it was a wchar_t System.Array, and the implication was Fortran supported converting .net arrays to fortran arrays via cptr as if they were native. Did I fall off the turnip truck again?

 

using namespace System;
using namespace System::IO::MemoryMappedFiles;
using namespace System::IO;
using namespace System::Runtime::InteropServices;

public ref class Fmmf {
public: static MemoryMappedFile^ mmf(String^ sID, int ilength, String^ sdisk) {
    MemoryMappedFile^ mmfx;
    long long llen = -1;
    try
    {
        mmfx = MemoryMappedFile::OpenExisting(sID);
    }
    catch (FileNotFoundException^ e) {
        try
        {
            mmfx = MemoryMappedFile::CreateFromFile(sdisk, FileMode::OpenOrCreate, sID, ilength);
            // file not preexist
            MemoryMappedViewAccessor^ accessor = mmfx->CreateViewAccessor(0, ilength);
            int inew = accessor->ReadInt32(0);
            //file just created
            if (inew != 0x00545257)
            {
                array <Byte>^ aBini = gcnew array <Byte>(ilength);
                aBini[0] = (Byte)'W'; aBini[1] = (Byte)'R'; aBini[2] = (Byte)'T'; aBini[3] = 0;
                for (int i = 4; i < ilength; i++) aBini[i] = (Byte)'A';
                for (int i = 4; i < ilength; i++) accessor->WriteArray(0, aBini, 0, ilength);
                accessor->Flush();
                //mmfx = MemoryMappedFile.CreateOrOpen(sID, ilength);
            }

        }
        catch (Exception^ e)
        {
            mmfx = nullptr;
            Console::WriteLine(e->Message);
        }
    }
    if (mmfx != nullptr)
    {
        FileInfo^ fi = gcnew FileInfo(sdisk);
        llen = fi->Length;
    }
    return(mmfx);
}
};
//DllImportAttribute
[DllImport("D:\\c\\vs2022\\mmff\\fDll1\\x64\\Debug\\fdll1.dll")]
extern int test3(array<wchar_t>^ aC,int *ilength, int *immflen);
int main(array<System::String ^> ^args){
    int fixsz = 0x100;
    int ilen = 0;
    String ^smf = "test_so";
    array <wchar_t>^ wac = smf->ToCharArray();
    Array::Resize<wchar_t>(wac, wac->Length + 1);
    wac[wac->Length - 1] = 0;

    String ^sdf = "D:\\c\\vs2022\\mmff\\mmfcs\\mmfdf" + "\\" + smf;
    MemoryMappedFile ^mmf = Fmmf::mmf(smf, fixsz, sdf);
    MemoryMappedViewAccessor ^mmfvw = mmf->CreateViewAccessor();
    unsigned char * pmmf = nullptr;
    mmfvw->SafeMemoryMappedViewHandle->AcquirePointer(pmmf);
    pmmf[8] = (char)'P';
    ilen = wac->Length;
    int ix = test3(wac, &ilen,&fixsz);
    mmfvw->SafeMemoryMappedViewHandle->ReleasePointer();
    //
    //while (true) {
    //    mmfvw.Flush();
    //    Thread.Sleep(180000);
    //}
    return 0;
}
[DllImport("D:\\c\\vs2022\\mmff\\fDll1\\x64\\Debug\\fdll1.dll")]
        static extern unsafe int test3(  char[] aC, ref int ilength, ref int immflen);
...
int immf_len = 42;
System.IO.MemoryMappedFiles.MemoryMappedFile mmf =
	 System.IO.MemoryMappedFiles.MemoryMappedFile.CreateNew("testf", immf_len);
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);

and Fortran

!  fdll1.f90 
 !
 !  FUNCTIONS/SUBROUTINES exported from fdll1.dll:
 !   
 !   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, volatile :: 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
3 Replies
Steve_Lionel
Honored Contributor III
552 Views

It's not a case of "converting". Depending on how you declare the argument in the .NET language, simple arrays may be passed by address. Otherwise, they tend to be passed as "SafeArrays". The IFCOM module contains routines for accessing SafeArrays - there is a VB sample in the Intel Fortran samples bundle. I have not looked closely at the code you posted.

0 Kudos
MWind2
New Contributor III
538 Views

My first mistake was I thought a C# char was the same as cpp char.  The C# code does not compile if the "usingSystem.Runtime.InteropServices;" is omitted. I think I will make simple example and look at call stack for char[] pass only.

Suspicion is Interop passes SafeArray to Fortran.

0 Kudos
JohnNichols
Valued Contributor III
531 Views

String ^sdf = "D:\\c\\vs2022\\mmff\\mmfcs\\mmfdf" + "\\" + smf;

 

Suggest you try path combine and @ and it will be a lot easier to read.  Also using a subst z: for your long name makes it easy to find.  

Just a thought.  

Pity that c# and Fortran are so alike and not alike.

0 Kudos
Reply