Calling Fortran DLL in C# with String arguments - c#

I am trying to pass a string variable from c# to fortran dll.
My C# code;
namespace fortrandll
{
class Program
{
[DllImport(#"csbdll10.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, EntryPoint = "sub")]
public static extern void sub(ref StringBuilder dbname, ref int n);
static void Main(string[] args)
{
StringBuilder dbname = new StringBuilder(5);
dbname.Append("dbname");
int n = 5;
sub(ref dbname, ref n);
Console.ReadLine();
}
}
}
Fortran code;
subroutine sub(dbname,leng) bind(C,name="sub")
use iso_c_binding
!DEC$ ATTRIBUTES reference :: dbname
!DEC$ ATTRIBUTES VALUE :: len
implicit none
integer, intent(in)::leng
character, intent(in):: dbname(leng)
write(*,*) dbname
return
end
Both the programs are compiled successfully.
But when I run the C# code getting some ascii code as result of fortran call rather intended output.
When I tried without ref keyword in C# getting Access Violation Exception.
Other things tried are Unmanaged datatypes and char bytes.
Thanks for the help.
Update: After removing 'ref' keyword for Stringbuilding and in functional call, getting the expected output.
Update Code:
class Program
{
[DllImport(#"csbdll10.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, EntryPoint = "sub")]
public static extern void sub(StringBuilder dbname, ref int n);
static void Main(string[] args)
{
StringBuilder dbname = new StringBuilder();
dbname.Append("dbname name is here");
int n = dbname.Length;
sub(dbname, ref n);
Console.ReadLine();
}
}

Related

Unable to get result of std::string function in C++ to C# Interop

What I am trying to achieve: I am trying to access a C++ application's functions through its DLL in a C# (Interop).
Issue which I am facing right now: When i create a std::string return type function in C++ and calls it through its DLL in C# code there is now output at all.
Code which I have written for C++ APP
extern "C"
{
__declspec(dllexport) int SM_Interop_API_Add(int a, int b)
{
return a + b;
}
__declspec(dllexport) std::string SM_Interop_API_getLanguage()
{
return "This is Test String";
//return getLanguage();
}
}
Code which I have written for C# APP
class Program
{
[DllImport(#"CPPSample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi,EntryPoint = "SM_Interop_API_Add")]
public static extern int SM_Interop_API_Add(int a, int b);
[DllImport(#"CPPSample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "SM_Interop_API_getLanguage")]
public static extern string SM_Interop_API_getLanguage();
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
int sum = SM_Interop_API_Add(10, 10);
Console.WriteLine($"Result: {sum}");
string result = SM_Interop_API_getLanguage();
Console.WriteLine($"Sm Language: {result}");
Console.WriteLine("----EOP----");
}
}
In the above code for C++, SM_Interop_API_getLanguage is supposed to return a string, and when I calls it through its DLL in C# code it does not return any result at all, first, I tried by returning the actual output of getLanguage which did not work then I tried to return some hard codded string but that is also coming as output.
Also to mention the function with int return type works perfectly only std::string return type is not working here in my case.
Kindly guide me if I am doing anything wrong or missing anything. Any help is appreciated.
std::string will never work here as the marshaller does not know how to free it.
Instead, you need to pass in a buffer from the C# side, like this
class Program
{
[DllImport(#"CPPSample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi,EntryPoint = "SM_Interop_API_Add")]
public static extern int SM_Interop_API_Add(int a, int b);
[DllImport(#"CPPSample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "SM_Interop_API_getLanguage")]
public static extern void SM_Interop_API_getLanguage(StringBuilder buffer)
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
int sum = SM_Interop_API_Add(10, 10);
Console.WriteLine($"Result: {sum}");
var result = new StringBuilder(200);
SM_Interop_API_getLanguage(result);
Console.WriteLine($"Sm Language: {result}");
Console.WriteLine("----EOP----");
}
}
Then on the C++ side, you simply copy it in (not great with C++, guessing a bit)
__declspec(dllexport) void SM_Interop_API_getLanguage(char* buffer)
{
strcpy(buffer, "This is Test String");
//return getLanguage();
}
Passing a buffer size and checking that it's large enough would be wise also.

C++ DLL LPCTSTR to C# string

I am trying to get string from C++ DLL to C#.
It outputs incorrect symbols - {栠搂珯獲긋ݳݳ贈琹玴ɡݳ⻜}
Here is my code:
C++ DLL
_declspec(dllexport) int __stdcall myClass(LPCTSTR& z)
{
z = _T("Test String");
return 0;
}
My C# code reading C++ DLL:
[DllImport("ecrClassDll.dll", CharSet = CharSet.Unicode)]
static extern void myClass(StringBuilder z);
static void Main(string[] args)
{
StringBuilder z = new StringBuilder();
myClass(z);
}
First, make sure you defined the UNICODE macro in C++, so that _T outputs wchar_t data, and LPCTSTR means const wchar_t*. That's what CharSet.Unicode expects. By the way, if you don't intend to support an ANSI version too, I wouldn't bother with all this _T stuff and just use Unicode everywhere, the code will be simpler.
Also, your C++ function returns an int, but your C# function expects a void. You have a mismatch there (unless you intended to set PreserveSig to false).
On the C# side, when you provide a StringBuilder, it means you provide a buffer to the C++ side, and you expect it to fill that buffer. The correct usage would be something like this:
_declspec(dllexport) int __stdcall myClass(LPCTSTR z, int zSize)
{
_tcscpy_s(z, zSize, _T("Test String"));
return 0;
}
[DllImport("ecrClassDll.dll", CharSet = CharSet.Unicode)]
static extern int myClass(StringBuilder z, int zSize);
static void Main(string[] args)
{
StringBuilder z = new StringBuilder(256);
myClass(z, z.Capacity);
}
But your code returns a pointer to a static string, which the marshaller doesn't expect here.
If you'd like to keep your C++ code as-is, you could try this instead:
[DllImport("ecrClassDll.dll", CharSet = CharSet.Unicode)]
static extern int myClass(out string z);
static void Main(string[] args)
{
string z;
myClass(out z);
}
I admit I didn't test it, but it should work as this C# signature matches the C++ signature.
If everything fails, you could try to marshal the data by yourself:
[DllImport("ecrClassDll.dll")]
static extern unsafe int myClass(void** z);
static unsafe void Main(string[] args)
{
void* z;
myClass(&z);
var str = Marshal.PtrToStringUni(new IntPtr(z));
}

Call Delphi Function From C#

I have a below DLL source code.
library Project1;
uses
System.SysUtils,
System.Classes;
type
IStringFunctions = interface
['{240B567B-E619-48E4-8CDA-F6A722F44A71}']
function GetMethodValueAsString():PAnsiChar; stdcall;
end;
TStringFunctions = class(TInterfacedObject, IStringFunctions)
public
function GetMethodValueAsString():PAnsiChar; stdcall;
end;
{$R *.res}
function TStringFunctions.GetMethodValueAsString():PAnsiChar; stdcall;
begin
Result := 'test';
end;
procedure GetImplementation(out instance:IStringFunctions); stdcall; export;
begin
instance := TStringFunctions.Create;
end;
exports GetImplementation;
begin
end.
I want to using in C# like this
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
[ComVisible(true)]
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("240B567B-E619-48E4-8CDA-F6A722F44A71")]
public interface IStringFunctions
{
[MethodImplAttribute(MethodImplOptions.PreserveSig)]
[return: MarshalAs(UnmanagedType.AnsiBStr)]
string GetMethodValueAsString();
}
class Program
{
[DllImport("kernel32.dll", EntryPoint = "LoadLibrary", CallingConvention = CallingConvention.StdCall)]
static extern int LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
static extern IntPtr GetProcAddress(int hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
[DllImport("kernel32.dll", EntryPoint = "FreeLibrary", CallingConvention = CallingConvention.StdCall)]
static extern bool FreeLibrary(int hModule);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
delegate void GetImplementation([MarshalAs(UnmanagedType.Interface)] out IStringFunctions instance);
static void Main(string[] args)
{
const string dllName = "Project1.dll";
const string functionName = "GetImplementation";
int libHandle = LoadLibrary(dllName);
if (libHandle == 0) throw new Exception(string.Format("Could not load library \"{0}\"", dllName));
var delphiFunctionAddress = GetProcAddress(libHandle, functionName);
if (delphiFunctionAddress == IntPtr.Zero) throw new Exception(string.Format("Can't find function \"{0}\" in library \"{1}\"", functionName, dllName));
GetImplementation getImplementation = (GetImplementation)Marshal.GetDelegateForFunctionPointer(delphiFunctionAddress, typeof(GetImplementation));
if (getImplementation != null)
{
IStringFunctions instance = null;
getImplementation(out instance);
if (instance != null)
{
//!!! don't return value !!!!
String result = instance.GetMethodValueAsString();
Console.WriteLine(result);
}
}
Console.ReadLine();
}
}
}
But instance.GetMethodValueAsString method doesn't working. And exit code.
I want to use returning value from dll function(GetMethodValueAsString) in c#.
I don't understand.
Where's my fault?
Thank you so much
[return: MarshalAs(UnmanagedType.AnsiBStr)]
This is wrong. You are not returning an ANSI encoded string, allocated on the COM heap. You are returning a plain C string, a pointer to null-terminated array of ANSI characters.
Your interface declaration should be:
[MethodImplAttribute(MethodImplOptions.PreserveSig)]
IntPtr GetMethodValueAsString();
Calling the method must be done like this:
IntPtr ptr = instance.GetMethodValueAsString();
string result = Marshal.PtrToStringAnsi(ptr);
Of course, your interface design becomes rather impractical when you need to return a dynamically allocated string. You'd need to export a deallocator too. The clean way to deal with this is to use a BSTR. Like this:
Delphi
IStringFunctions = interface
['{240B567B-E619-48E4-8CDA-F6A722F44A71}']
procedure GetMethodValueAsString(out value: WideString); stdcall;
end;
C#
[MethodImplAttribute(MethodImplOptions.PreserveSig)]
void GetMethodValueAsString([MarshalAs(UnmanagedType.BStr)] out string result);
Is that Delphi DLL visible to your C# code for COM Interop? If not, the easiest way to do this would be to attach the dll to the C# class library project using the "Add Existing Item" menu option. Then in the properties window for this dll set "BuildAction" to None, and "Copy to Output Directory" to "Copy always"
Then you could do something like this in the C# code.
[DllImport("Project1.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern string GetMethodValueAsString();
Then wherever you need to call that function you could do
var outputMessage = GetMethodValueAsString();
Console.WriteLine(outputMessage);

C# Calling C function crash when using LoadLibrary/GetProcAddress

I need to dynamically load DLL and invoke its methods
C code header:
__declspec(dllexport) int Init_Normalization_EN(char* path);
__declspec(dllexport) const char* Process_Normalization_EN(char* input);
C# code using [extern] to statically define library and methods:
[DllImport("TextNormalization_EN.dll", EntryPoint = "?Init_Normalization_EN##YAHPAD#Z", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int Init_Normalization_EN(IntPtr path);
[DllImport("TextNormalization_EN.dll", EntryPoint = "?Process_Normalization_EN##YAPBDPAD#Z", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern IntPtr Process_Normalization_EN(IntPtr input);
When these declarations are used, interop works fine (for both init and process of normalization), but I need to point to a DLL dynamically, so I use the following code:
in the class-level:
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
private delegate int CallInit(IntPtr ipFolder);
private CallInit Init = null;
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
private delegate IntPtr CallNormalize(IntPtr ipInput);
private CallNormalize Normalize = null;
in the constructor:
IntPtr pDll = NativeMethods.LoadLibrary(libraryPath);
IntPtr pAddressOfInit = NativeMethods.GetProcAddress(pDll, InitName);
Init = (CallInit)Marshal.GetDelegateForFunctionPointer(pAddressOfInit, typeof(CallInit));
IntPtr pAddressOfNormalize = NativeMethods.GetProcAddress(pDll, NormalizeName);
Normalize = (CallNormalize)Marshal.GetDelegateForFunctionPointer(pDll, typeof(CallNormalize));
IntPtr pFolder = Marshal.StringToCoTaskMemAnsi(dataFolderPath);
int result = this.Init(pFolder);
if (result != 0)
{
InitializeCompleted = true;
}
all this code runs OK and even the call to init the normalizer with a folder-path works fine (returns a handle non-zero)
but
when I try to run the text-normalizer:
IntPtr pInput = Marshal.StringToCoTaskMemAnsi(text);
IntPtr pResult = this.Normalize(pInput);
I get on the second line an application-level exception (that cannot be caught by try/catch):
"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
Which is as far as I can understand caused by the returned string which I try to get as IntPtr as in the [extern] declaration
Shouldn't this line:
Normalize = (CallNormalize)Marshal.GetDelegateForFunctionPointer(
pDll,
typeof(CallNormalize));
be
Normalize = (CallNormalize)Marshal.GetDelegateForFunctionPointer(
pAddressOfNormalize,
typeof(CallNormalize));

Array change in C++ function is not preserved in C#

I have a C# code calling a C++ function.
The C++ function should fill a buffer passed with a pointer. However, the array returns empty.
The import declaration is:
[DllImport("ProjectLogicInterface", EntryPoint = "FillArr", CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 FillArr(char[] arr);
The code, after simplifications and entering some hard coded values looks like that:
The code in C#:
char[] arr= new char[10];
ret = LogicInterface.FillArr(arr);
The C++ code:
bool FillArr(char* arr)
{
int length=10;
for(int i = 0; i < length; i++)
{
arr[i] = 3; //replaced with some hard coded value
}
return true;
}
However, the array remains empty.
Any suggestions?
I believe that you would have to pin the array before passing it to your native code. This prevents the GC from moving the managed array around in memory while you're accessing it in C++.
So you could use:
[DllImport("ProjectLogicInterface", EntryPoint = "FillArr", CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 FillArr( char* arr );
And then:
char[] arr = new char[ 10 ];
fixed (char* pinned = arr )
{
ret = LogicInterface.FillArr( pinned );
}
Note that I haven't actually compiled and run this but it should give you some ideas on how to proceed.
You don't need to pin the array as long as the code in C++ doesn't cache it before returning.
In your case, you need to pass the array by reference so it can be considered as an out argument.
[DllImport("ProjectLogicInterface", EntryPoint = "FillArr", CallingConvention = CallingConvention.Cdecl)]
public static extern UInt32 FillArr(ref char[] arr);`
Found this link highly usefull:
C#: calling C++ DLL with char** argument
The final code looks like:
[DllImport("ProjectLogicInterface", EntryPoint = "FillArr", CallingConvention = CallingConvention.Cdecl)]
static extern bool FillArr([MarshalAs(UnmanagedType.LPStr, ArraySubType = UnmanagedType.LPStr)] StringBuilder args);
static void Main(string[] args)
{
StringBuilder arr = new StringBuilder(9);
bool res = FillArr(arr);
}

Categories