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

String returned from C# to Fortran - problem

Mark_Besley
Beginner
1,333 Views

I have an Intel Fortran program that calls a C# function. The C# function header is a follows, it takes "stringresourcekey" and returns "stringresource" (1024 byte string) and two integers.

private void GetStringResource(
string stringresourcekey,
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeConst = 1024)] byte[] stringresource,
ref int stringresourcelength_ansi,
ref int stringresourcelength_utf8)

In Fortran, I call it like this:

call GetStringResourceCallbackExternal(stringresourcekey,sStringResource,length_ansi,length_utf8)

Declarations are:

character

(len=*), intent(in) :: stringresourcekey
character (len=1024) :: sStringResource,out_string
integer :: &
length_ansi, &
! ANSI String length
length_utf8 ! Effective String length when displayed with UTF-8 encoding

This all works fine, the C# function is called, executes properly, and appears to return the intended values. Running in the debugger, I can see sStringResource,length_ansi,length_utf8 all returned with correct values and the intended string in sStringResource.

However when I try to use sStringResource, I have problems - if I write it to a file, the first part of it is garbage. If I try a simple assignment:

out_string = sStringResource

then the program crashes with an access violation. Any ideas what I'm doing wrong (or not doing)?

Mark Besley

0 Kudos
11 Replies
onkelhotte
New Contributor II
1,333 Views
In C# I use StringBuilder to exchange strings with Fortran.
Here are two examples to get and put a character*4 value:
C#
[c-sharp]	public static void putusername(string value)
	{
		StringBuilder stringBuilderValue = new StringBuilder(value.Substring(0, System.Math.Min(value.Length, 4)));
		PUT_USERNAME(stringBuilderValue, 4);
	}

	public static string getusername()
	{
		StringBuilder stringBuilderValue = new StringBuilder(4);
		GET_USERNAME(stringBuilderValue, 4);
		return stringBuilderValue.ToString();
	}[/c-sharp]
[c-sharp][/c-sharp]
[c-sharp][/c-sharp]
[c-sharp]Fortran:[/c-sharp]
[c-sharp]
	subroutine put_dateiname(value)
		use globaleVariablen
		implicit none
		character*255 dateiname
		dateiname = value
	end subroutine put_dateiname
!
	subroutine put_username(value)
		use globaleVariablen
		implicit none
		character*4 username
		username = value
	end subroutine put_username[/c-sharp]
0 Kudos
onkelhotte
New Contributor II
1,333 Views
Why cant I edit my post?
Here is the Fortran part again:
[cpp]	subroutine put_dateiname(value)
		use globaleVariablen
		implicit none
		character*255 dateiname
		dateiname = value
	end subroutine put_dateiname
!
	subroutine put_username(value)
		use globaleVariablen
		implicit none
		character*4 username
		username = value
	end subroutine put_username[/cpp]
Markus
0 Kudos
onkelhotte
New Contributor II
1,333 Views
Damn, I posted the wrong Fortran code... Its too early in the morning and I havent had any coffee yet ;-)
globaleVariablen is a module where username is declared as character*4. In the C# part you have to name the length of the character, so you write get_username(stringBuilderValue, 4),although the Fortran function doesnt have a second parameter.

[cpp]	subroutine put_username(value)
		use globaleVariablen
		implicit none
		character*4 username
		username = value
	end subroutine put_username
!
	character*4 function get_username()
		use globaleVariablen
		implicit none
		getusername = username
	end function get_username[/cpp]
Markus
0 Kudos
Mark_Besley
Beginner
1,333 Views
Quoting - Mark Besley

I have an Intel Fortran program that calls a C# function. The C# function header is a follows, it takes "stringresourcekey" and returns "stringresource" (1024 byte string) and two integers.

private void GetStringResource(
string stringresourcekey,
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeConst = 1024)] byte[] stringresource,
ref int stringresourcelength_ansi,
ref int stringresourcelength_utf8)

In Fortran, I call it like this:

call GetStringResourceCallbackExternal(stringresourcekey,sStringResource,length_ansi,length_utf8)

Declarations are:

character

(len=*), intent(in) :: stringresourcekey
character (len=1024) :: sStringResource,out_string
integer :: &
length_ansi, &
! ANSI String length
length_utf8 ! Effective String length when displayed with UTF-8 encoding

This all works fine, the C# function is called, executes properly, and appears to return the intended values. Running in the debugger, I can see sStringResource,length_ansi,length_utf8 all returned with correct values and the intended string in sStringResource.

However when I try to use sStringResource, I have problems - if I write it to a file, the first part of it is garbage. If I try a simple assignment:

out_string = sStringResource

then the program crashes with an access violation. Any ideas what I'm doing wrong (or not doing)?

Mark Besley

A further development - I changed nothing in the way that the calls were done and changed nothing at the C# end, but instead declared sStringResource as an array of 256 4-byte integers:

use the same call:
call GetStringResourceCallbackExternalstringresourcekey,sStringResource,length_ansi,length_utf8)

integer, dimension(256) :: sStringResource,out_string
character (len=1024) :: mold,out_string1

out_string = sStringResource

I can assign this to another integer array without getting an access violation, and better still I can transfer the data into a character variable and write it out and get the intended output as follows:

Any idea why this fails if I declare the sStringResource as character but works fine if I declare it as integer? Is there some subtle difference in the way the data is handled between the two types?

out_string1 =

transfer(out_string,mold)
write(unlis1,"(a)")out_string1

Mark Besley

0 Kudos
anthonyrichards
New Contributor III
1,333 Views
If you are sending strings toa program written with any type of 'C'- language, do you not have to terminate each string with the null (CHAR(0)) character first? That's how C knows where the string ends. Any string length argument returned by Fortran must therefore include the terminating null character in the count so the C-side string length must match or exceed this length.
Also, whenever a 'C'-type program sends a reference to a string, the string will be terminated with a null character too, so looking for the null characterlets you find the string's length.
0 Kudos
pieterj
Beginner
1,333 Views

Hallo everybody

It looks like you are familiar with calling C# from Fortran. Can you please help me with my project I attached?

Im not able link C# dll. I created interface (suppressed in code) but it doesnt work. In help is written that for .NET dll is necessary use Module Wizard for creating interface. But when I used this Wizard no interfaces crated (see. ShowNumberInterface.f90).

Can you please tell me what is necessary for making right Fortran C# mixed language solution?

I use VS2003.NET and IVF 10.1.019.

Jiri

0 Kudos
pieterj
Beginner
1,333 Views
Ops, and nowthis file:o)
0 Kudos
anthonyrichards
New Contributor III
1,333 Views
Quoting - pieterj
Ops, and nowthis file:o)

I do not know C#, but I do know that if you want a Fortran program to call a function in a DLL, then

a) you must make the function or symbol for the functionvisible to users of the DLL, and
b) In your Fortran code, you must use the correct symbol for the function you wish to access in the DLL, possibly using a compiler flag such as ALIAS, and
c) In your Fortran code, you must mark that symbol as to be IMPORTED from a dll using a compiler flag such as DLLIMPORT,
d) You must also, in your Fortran, ensure that the calling convention agrees with the C# one (e.g. are the arguments supplied by value or reference (i.e. address)

In your C# code,

[cpp]using System;
using System.Windows.Forms;

namespace prijimac
{
	public class Class1
	{

		public static void ShowNumber(int a, int b)
		{
			MessageBox.Show("a=" + a.ToString()+ " b=" + b.ToString());
		}

	}
}
[/cpp]

There are no symbols exported, correct?. Thus, as far as I can see from your zipped archive, there is no export library (.LIB) of symbols generated and supplied along with your C# DLL, so there is no way for your Fortran program to be given the details as to what and whereare the symbols that you wish to reference in your Fortran code (normally you would include the .LIB in your Fortran project to do this). This shortcoming has to be rectified first before you stand a chance of calling functions in your DLL, IMO.

0 Kudos
g_f_thomas
Beginner
1,333 Views

I agree with Anthony's assessment.

Exporting from a C# DLL is nonstandard and has been discussed (my me) here on this forum some time ago.

Gerry

0 Kudos
anthonyrichards
New Contributor III
1,333 Views
Essentially, you have to reproduce in C# what is given below as the code for a C++ DLL (given the name mycdll for example) containing one function MyCDllRoutine. Note that given the calling convention used, this will export a symbol _MyCDllRoutine@8 in the generated symbol table mycdll.lib (note the preservation of case, theadded leading underscore and thedecoration @8). The Fortran code to produce a Console application (so that you can write to the screen) that calls the DLL is given below the C++ code. In order for the Fortran program to work, the file mycdll.lib must be added to the Fortran project and the mycdll.dll code added to the folder containing the Fortran executable.
[cpp]// mycdll.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"

// myDLL.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
#include "iostream.h"
#include "stdlib.h"

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
      )
{
    return TRUE;
}

   extern "C" __declspec(dllexport)  void  __stdcall MyCDllRoutine(int a, int b) ;


// MAIN ENTRY FUNCTION
void  __stdcall MyCDllRoutine (int a, int b)
{
	char achar[10], bchar[10], str[80];

itoa(a,achar,10); 
itoa(b,bchar,10);
strcpy(str," a = ");
strcat(str,achar);
strcat(str,", b = ");
strcat(str,bchar);
cout <<" Entered MyCDllRoutine in the C++ DLL"< [/cpp]
[cpp]Fortran Code:[/cpp]
[cpp]
!****************************************************************************
!
!  PROGRAM: testmycdll
!
!  PURPOSE:  Entry point for 'Hello World' sample console application to access
!			 a routine held in a Win32 Dynamic link library compiled from C++ code
!
!****************************************************************************

	program testmycdll

implicit none
!
INTEGER I,J

       INTERFACE
       SUBROUTINE MYCDLLROUTINE (I,J)

       !DEC$ ATTRIBUTES DLLIMPORT :: MYCDLLROUTINE
       !DEC$ ATTRIBUTES STDCALL :: MYCDLLROUTINE
       !DEC$ ATTRIBUTES ALIAS:'_MyCDllRoutine@8' :: MYCDLLROUTINE
	   INTEGER I,J

       END SUBROUTINE MYCDLLROUTINE
       END INTERFACE

 print *, 'Hello World'
 I=10
 J=25

       CALL MYCDLLROUTINE(I,J)

	end program testmycdll
[/cpp]

0 Kudos
anthonyrichards
New Contributor III
1,333 Views
The following was somehow chopped off the end of the C++ code:
[cpp]cout <<" Entered MyCDllRoutine in the C++ DLL"< [/cpp]
[cpp]MessageBox(HWND_DESKTOP,str,"Two numbers sent to C++ DLL",MB_OK);
}

[/cpp]
0 Kudos
Reply