Forum Jump

Select Group :
Select Forum :
Sorted By :
Sort Order :
From The :
 
Thread Tools  Search this thread 
Mark Besley
Total Points:
30
Registered User
November 24, 2008 9:08 PM PST
String returned from C# to Fortran - problem

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

 

onkelhotte
Total Points:
2,700
Status Points:
2,200
Brown Belt
November 24, 2008 10:45 PM PST
Rate
 
#1
In C# I use StringBuilder to exchange strings with Fortran.
Here are two examples to get and put a character*4 value:
C#
	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();
	}


Fortran:
	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


onkelhotte
Total Points:
2,700
Status Points:
2,200
Brown Belt
November 24, 2008 10:49 PM PST
Rate
 
#2 Reply to #1
Why can´t I edit my post?
Here is the Fortran part again:
	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
Markus


onkelhotte
Total Points:
2,700
Status Points:
2,200
Brown Belt
November 24, 2008 10:56 PM PST
Rate
 
#3 Reply to #2
Damn, I posted the wrong Fortran code... It´s too early in the morning and I haven´t 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 doesn´t have a second parameter. 

 

 

	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
Markus


Mark Besley
Total Points:
30
Registered User
November 26, 2008 2:12 AM PST
Rate
 
#4
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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



anthonyrichards
Total Points:
3,637
Status Points:
3,137
Brown Belt
November 27, 2008 1:05 AM PST
Rate
 
#5 Reply to #4
If you are sending strings to a 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 character lets you find the string's length.


pieterj
Total Points:
40
Registered User
November 27, 2008 3:42 AM PST
Rate
 
#6 Reply to #5

Hallo everybody

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

I’m not able link C# dll. I created interface (suppressed in code) but it doesn’t 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



pieterj
Total Points:
40
Registered User
November 27, 2008 3:46 AM PST
Rate
 
#7 Reply to #6
Ops, and now this file :o) 


 Attachments 
anthonyrichards
Total Points:
3,637
Status Points:
3,137
Brown Belt
November 27, 2008 5:08 AM PST
Rate
 
#8 Reply to #7
Quoting - pieterj
Ops, and now this 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 function visible 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,

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());
		}

	}
}

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 where are 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.



g.f.thomas
November 27, 2008 6:13 AM PST
Rate
 
#9 Reply to #8

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

 



anthonyrichards
Total Points:
3,637
Status Points:
3,137
Brown Belt
November 27, 2008 8:24 AM PST
Rate
 
#10 Reply to #9
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, the added leading underscore and the decoration @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.
// 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"< 
Fortran Code:
!****************************************************************************
!
!  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



anthonyrichards
Total Points:
3,637
Status Points:
3,137
Brown Belt
November 27, 2008 8:28 AM PST
Rate
 
#11 Reply to #10
The following was somehow chopped off the end of the C++ code:
cout <<" Entered MyCDllRoutine in the C++ DLL"< 
MessageBox(HWND_DESKTOP,str,"Two numbers sent to C++ DLL",MB_OK);
}





Intel Software Network Forums Statistics

6668 users have contributed to 28284 threads and 87462 posts to date.
In the past 24 hours, we have 5 new thread(s) 32 new posts(s), and 43 new user(s).

In the past 3 days, the most popular thread for everyone has been Fortran and Matlab The most posts were made to Larger Test Data The post with the most views is Quoting - nabeels Hello e

Please welcome our newest member karolbe