Array change in C++ function is not preserved in C# - 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);
}

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# interop returning System.Access.Violation

I am attempting to use a C function through C# Interop and I am receiving an access violation on one function. I have tried a number of things and I can't seem to solve this.
Here is the C code that needs to be changed into c# code:
typedef struct
{
char SerNo[64];
unsigned char hwVer;
HANDLE device; // Set by the API on return from SelectDevice()
} DeviceT;
This struct is used by the following function:
error = GetDevices(DeviceT *devices, unsigned int *numDevs, unsigned int maxDevs)
There is one other function in the C code:
error = SelectDevice(DeviceT *device)
So I began by defining DeviceT. I tried a few ways, but settled on this since it is simple:
[StructLayout(LayoutKind.Sequential)]
public struct DeviceT
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public char[] SerNo;
public byte hwVer;
public IntPtr device;
}
The GetDevices function was set to this:
[DllImport("file.dll", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern ErrT GetDevices([In, Out] DeviceT[] devices, uint* numDevs, uint maxDev);
The SelectDevices function was set to this:
[DllImport("file.dll", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern ErrT SelectDevice([In, Out] DeviceT devices);
The code goes like this:
uint numDevs = 6;
uint maxDev = 6;
uint chosenIdx = 0;
DeviceT[] devices = new DeviceT[6];
err = GetDevices(devices, &NumberOfDevices, maxDev))
At this point everything is correct. The devices array has the correct information in it.
I now continue with (I just hard code select the first device)
chosenIdx = 0;
var chosenDevice = devices[chosenIdx];
err = SelectDevice(chosenDevice);
This last function returns a System.Access Violation
I tried a whole bunch of things but all end up with the same result. I suspect it has something to do with the HANDLE but I am not sure.
Thanks for any help.
SelectDevice takes a DeviceT *, but your P/Invoke signature takes a DeviceT. That is, you're passing in DeviceT by value rather than passing a pointer.
Try:
[DllImport("file.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern ErrT SelectDevice([In, Out] ref DeviceT devices);
err = SelectDevice(ref chosenDevice);

Calling Fortran DLL in C# with String arguments

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

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

How do I convert from an unmanaged C++ dll char** to a C# string and back

I am trying to call a function on a unmanaged C++ DLL, searching stackoverflow posts I came up close but I cant get it to fully work.
With a declaration in the .h file as follows:
extern int SomeDLLMethod(const char **data, int *count);
the data is a string
I have declared in C# as follows:
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int SomeDLLMethod(IntPtr data, ref int count);
Then i can call it from C# as follows:
unsafe
{
fixed (byte* buffer = new byte[MAX_LENGTH])
{
IntPtr ptr = new IntPtr(buffer);
int count = 0;
var retVal = SomeDLLMethod(ptr, ref count);
var dataString = Marshal.PtrToStringAuto(ptr);
Console.WriteLine(dataString);
}
}
The call succeeds, there is a count and data in buffer, but how do I read this value back to C# string?
The Marshal methods is giving me garbage
There's not enough information in the question to be 100% sure but my guess is that you need this:
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int SomeDLLMethod(ref IntPtr data, ref int count);
.....
IntPtr data;
int count;
int retval = SomeDLLMethod(ref data, ref count);
string str = Marshal.PtrToStringAnsi(data, count);
Ideally when asking a question like this you should include the full documentation of the native function. I say this because a char** can mean many different things.
My assumption is that the char** here is a pointer to a null-terminated C string allocated by the DLL. Your code assumes that the caller allocates the buffer but if that were so then I would expect to see char* rather than char**.

Categories