Return C++ array to C# - c#

I can't seem to figure out how to return an array from an exported C++ DLL to my C# program. The only thing I've found from googling was using Marshal.Copy() to copy the array into a buffer but that doesn't give me the values I'm trying to return, I don't know what it's giving me.
Here's what I've been trying:
Exported function:
extern "C" __declspec(dllexport) int* Test()
{
int arr[] = {1,2,3,4,5};
return arr;
}
C# portion:
[DllImport("Dump.dll")]
public extern static int[] test();
static void Main(string[] args)
{
Console.WriteLine(test()[0]);
Console.ReadKey();
}
I know the return type int[] is probably wrong because of the managed/unmanaged differences, I just have no idea where to go from here. I can't seem to find an answer for anything but returning character arrays to strings, not integer arrays.
I figured the reason the values I'm getting with Marshal.Copy are not the ones I'm returning is because the 'arr' array in the exported function gets deleted but I'm not 100% sure, if anyone can clear this up that would be great.

I have implemented the solution Sriram has proposed. In case someone wants it here it is.
In C++ you create a DLL with this code:
extern "C" __declspec(dllexport) int* test()
{
int len = 5;
int * arr=new int[len+1];
arr[0]=len;
arr[1]=1;
arr[2]=2;
arr[3]=3;
arr[4]=4;
arr[5]=5;
return arr;
}
extern "C" __declspec(dllexport) int ReleaseMemory(int* pArray)
{
delete[] pArray;
return 0;
}
The DLL will be called InteropTestApp.
Then you create a console application in C#.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace DLLCall
{
class Program
{
[DllImport("C:\\Devs\\C++\\Projects\\Interop\\InteropTestApp\\Debug\\InteropTestApp.dll")]
public static extern IntPtr test();
[DllImport("C:\\Devs\\C++\\Projects\\Interop\\InteropTestApp\\Debug\\InteropTestApp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int ReleaseMemory(IntPtr ptr);
static void Main(string[] args)
{
IntPtr ptr = test();
int arrayLength = Marshal.ReadInt32(ptr);
// points to arr[1], which is first value
IntPtr start = IntPtr.Add(ptr, 4);
int[] result = new int[arrayLength];
Marshal.Copy(start, result, 0, arrayLength);
ReleaseMemory(ptr);
Console.ReadKey();
}
}
}
result now contains the values 1,2,3,4,5.
Hope that helps.

More or less the same as the answer above, but this worked for me.
For an array returned from a c method like this:
EXPORT char** methodname(void);
I did not succeed in getting this to work with the MarshalAs attribute, but this did the trick: (condensed code)
[DllImport(libName, EntryPoint = "methodname", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr methodname();
var listPtr = methodname();
var list = new List<string>();
IntPtr itemPtr = IntPtr.Zero;
var offset = 0;
while ((itemPtr = Marshal.ReadIntPtr(listPtr, offset)) != IntPtr.Zero)
{
Console.WriteLine(Marshal.PtrToStringAnsi(itemPtr));
offset += 4;
}

Related

C# Sending String Array to C++ DLL as SafeArray. Gets only first character

I have C# project and I have to use a C++ dll using DllImport. (I have source codes of c++ dll)
I'm importing a function from c++ dll like this :
[DllImport("Example.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int SendRequest([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] ref string[] fields);
I'm using this function in C# like this :
List<String> fields = new List<String>();
fields.Add("Test1");
fields.Add("Test2");
string[] fieldsArr = fields.ToArray();
int resultOfSendRequest = SendRequest(ref fieldsArr);
Problem is in c++ dll, it casts string to char* in somewhere and it reads the only first character, not entire string.
How can I solve this without touching c++ dll. (If I call this function from VB6 it works without any problem.)
C++ code is like this :
VARIANT vVar;
__declspec( dllexport ) int _stdcall SendRequest (SAFEARRAY**);
int _stdcall SendRequest ( SAFEARRAY** arrayFlds,
short NFlds)
{
// *********** prepare O.i.d, fields name, values
for (long iElem=0; iElem < NFlds; iElem++)
if (LoadElement (&vVar, iElem, &flds[iElem], &pFlds[iElem],*arrayFlds)==-1)
return -1;
//...
}
int LoadElement( VARIANT* vVar,
long iElem,
S_FLDS* flds,
char** pFld,
SAFEARRAY* arrayFlds)
{
hRes = SafeArrayGetElement(arrayFlds, &iElem, pFld);
strcpy(flds->FieldName, *pFld);
flds->bValLen = 0;
char *Name = flds->FieldName;
//....
}
The fieldName and char *Name at the end of the core only consists of first character of string. Not the full string.
In C Langues a string is a byte[] with each string terminated with a '\0'. An array of strings the last item has two '\0' at the end. So try following :
[DllImport("Example.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int SendRequest(IntPtr fields);
static void Main(string[] args)
{
List<String> fields = new List<String>();
fields.Add("Test1");
fields.Add("Test2");
string fieldsArr = string.Join("\0", fields);
IntPtr fieldsPtr = Marshal.StringToBSTR(fieldsArr);
int results = SendRequest(fieldsPtr);
}
If it is an array of pointers then use this
public struct Pointers
{
public IntPtr[] pointers;
}
[DllImport("Example.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int SendRequest(IntPtr fields);
static void Main(string[] args)
{
List<String> fields = new List<String>();
fields.Add("Test1");
fields.Add("Test2");
List<IntPtr> pointers = new List<IntPtr>();
foreach (string field in fields)
{
IntPtr intPtr = Marshal.StringToBSTR(field);
pointers.Add(intPtr);
}
Pointers sPointers = new Pointers();
sPointers.pointers = pointers.ToArray();
IntPtr fieldsPtr = IntPtr.Zero;
Marshal.StructureToPtr(sPointers, fieldsPtr, true);
int results = SendRequest(fieldsPtr);
Marshal.FreeHGlobal(fieldsPtr);
}

AccessViolationException when Call C++ dll on C#

I am implementing a C++ Dll for C#. But when I run my program, an AccessViolationException will occur. Here is the code.
in C++:
extern "C" DLLIMPORT double __cdecl ErrorCalculate(double* pBufferA, double* pBufferB,__int32 length)
{
__m128d xfdLoadA;
__m128d xfdLoadB;
const double* pA = pBufferA;
const double* pB = pBufferB;
//do somthing
for(int i=0;i<length/2;i++)
{
xfdLoadA = _mm_load_pd(pA);//error occur at this line
xfdLoadB = _mm_load_pd(pB);
pA+=2;
pB+=2;
// do somthing
}
// do somthing
}
in C#:
[DllImport("test.dll", EntryPoint = "ErrorCalculate", CallingConvention = CallingConvention.Cdecl)]
static extern double ErrorCalculate(double[] pBufferA, double[] pBufferB, int length);
public void frameProcessing(Image<Gray, byte> frame){
//do something
SSE4 sse4 = new SSE4();
unsafe
{
//length of samplePoint is the same as tempPoint
fixed (double* sample = samplePoint)
{
fixed (double* test = tempPoint)
{
errorSum = sse4.ErrorCalc(sample, test, length);
}
}
}
if(errorSum<=threshold)
{//do something}
}
frameProcessing will be called several times. Sometimes the error will occur at first time I call frameProcessing, sometimes second time. And I found that error occur at _mm_load_pd(pA). I'm sure that pA and pB are not null(Value of the two array can be printed out). Any help would be appreciated.

Using GhostScript 9.10 in Windows with Unicode characters in parameters

I want to use Ghostscript in a .NET / C# application to convert a .tiff file to PDF.
My problem: When the file path contains non-ansi characters (e.g. Umlaute), the function
gsapi_init_with_args
fails. (With GS 8.x, it works fine!).
I found information that the behaviour was changed in 9.x, and I also found a function called
gsapi_init_with_argsW
And this function should work with .NET without any problems (see http://permalink.gmane.org/gmane.comp.printing.ghostscript.cvs/31721)
So I use the following DLLImport:
[DllImport(#"gsdll32.dll")]
public static extern int gsapi_init_with_argsW( IntPtr instace, int argc, string[] argv);
but this still does not work, I get the error:
Error: /undefinedfilename
in (C:\\304NDERUNGEN\\TEST.PDF)
The name of the file schould be
C:\\ÄNDERUNGEN\\TEST.PDF
so the umlaut "Ä" is not recognized correctly.
I´ve search the web a lot but did not found a solution.
Any idea?
Thank you!
I suspect that you will need to use UTF-8 here. Make a call to gs_set_arg_encoding passing GS_ARG_ENCODING_UTF8.
Any strings that you pass to Ghostscript should be declared as IntPtr. To convert from a C# string to a null-terminated UTF-8 encoded string use this function provided by Hans Passant:
public static IntPtr NativeUtf8FromString(string managedString)
{
int len = Encoding.UTF8.GetByteCount(managedString);
byte[] buffer = new byte[len + 1]; // null-terminator allocated
Encoding.UTF8.GetBytes(managedString, 0, managedString.Length, buffer, 0);
IntPtr nativeUtf8 = Marshal.AllocHGlobal(buffer.Length);
Marshal.Copy(buffer, 0, nativeUtf8, buffer.Length);
return nativeUtf8;
}
Make sure that you remember to clean up with a call to Marshal.FreeHGlobal.
The overall code might look a little like this:
public class Ghostscript
{
public const int GS_ARG_ENCODING_LOCAL = 0;
public const int GS_ARG_ENCODING_UTF8 = 1;
[DllImport("gsdll32.dll")]
private static extern int gsapi_new_instance(out IntPtr inst, IntPtr handle);
[DllImport("gsdll32.dll")]
private static extern int gsapi_set_arg_encoding(IntPtr inst, int encoding);
[DllImport("gsdll32.dll")]
private static extern int gsapi_init_with_args(IntPtr inst, int argc, IntPtr[] argv);
[DllImport("gsdll32.dll")]
private static extern int gsapi_exit(IntPtr inst);
[DllImport("gsdll32.dll")]
private static extern void gsapi_delete_instance(IntPtr inst);
private static void checkReturnValue(int retval)
{
if (retval != 0)
throw ...; // implement error handling here
}
public static void run(string[] argv)
{
IntPtr inst;
checkReturnValue(gsapi_new_instance(out inst, IntPtr.Zero));
try
{
IntPtr[] utf8argv = new IntPtr[argv.length];
for (int i=0; i<utf8argv.Length; i++)
utf8argv[i] = NativeUtf8FromString(argv[i]);
try
{
checkReturnValue(gsapi_set_arg_encoding(inst, GS_ARG_ENCODING_UTF8));
checkReturnValue(gsapi_init_with_args(inst, utf8argv.Length, utf8argv));
checkReturnValue(gsapi_exit(inst));
finally
{
for (int i=0; i<utf8argv.Length; i++)
Marshal.FreeHGlobal(utf8argv[i]);
}
}
finally
{
gsapi_delete_instance(inst);
}
}
}

C# call C++ DLL passing pointer-to-pointer argument

Could you guys please help me solve the following issue?
I have a C++ function dll, and it will be called by another C# application.
One of the functions I needed is as follow:
struct DataStruct
{
unsigned char* data;
int len;
};
DLLAPI int API_ReadFile(const wchar_t* filename, DataStruct** outData);
I wrote the following code in C#:
class CS_DataStruct
{
public byte[] data;
public int len;
}
[DllImport("ReadFile.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
private static extern int API_ReadFile([MarshalAs(UnmanagedType.LPWStr)]string filename, ref CS_DataStruct data);
Unfortunately, the above code is not working... I guess that is due to the C++ func takes a pointer-to-pointer of DataStruct, while I just passed a reference of CS_DataStruct in.
May I know how can I pass a pointer-to-pointer to the C++ func? If it is not possible, is there any workaround? (the C++ API is fixed, so changing API to pointer is not possible)
Edit:
Memory of DataStruct will be allocated by c++ function. Before that, I have no idea how large the data array should be.
(Thanks for the comments below)
I used the following test implementation:
int API_ReadFile(const wchar_t* filename, DataStruct** outData)
{
*outData = new DataStruct();
(*outData)->data = (unsigned char*)_strdup("hello");
(*outData)->len = 5;
return 0;
}
void API_Free(DataStruct** pp)
{
free((*pp)->data);
delete *pp;
*pp = NULL;
}
The C# code to access those functions are as follows:
[StructLayout(LayoutKind.Sequential)]
struct DataStruct
{
public IntPtr data;
public int len;
};
[DllImport("ReadFile.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
unsafe private static extern int API_ReadFile([MarshalAs(UnmanagedType.LPWStr)]string filename, DataStruct** outData);
[DllImport("ReadFile.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe private static extern void API_Free(DataStruct** handle);
unsafe static int ReadFile(string filename, out byte[] buffer)
{
DataStruct* outData;
int result = API_ReadFile(filename, &outData);
buffer = new byte[outData->len];
Marshal.Copy((IntPtr)outData->data, buffer, 0, outData->len);
API_Free(&outData);
return result;
}
static void Main(string[] args)
{
byte[] buffer;
ReadFile("test.txt", out buffer);
foreach (byte ch in buffer)
{
Console.Write("{0} ", ch);
}
Console.Write("\n");
}
The data is now transferred to buffer safely, and there should be no memory leaks. I wish it would help.
It isn't necessary to use unsafe to pass a pointer to an array from a DLL. Here is an example (see the 'results' parameter). The key is to use the ref attribute. It also shows how to pass several other types of data.
As defined in C++/C:
#ifdef __cplusplus
extern "C" {
#endif
#ifdef BUILDING_DLL
#define DLLCALL __declspec(dllexport)
#else
#define DLLCALL __declspec(dllimport)
#endif
static const int DataLength = 10;
static const int StrLen = 16;
static const int MaxResults = 30;
enum Status { on = 0, off = 1 };
struct Result {
char name[StrLen]; //!< Up to StrLen-1 char null-terminated name
float location;
Status status;
};
/**
* Analyze Data
* #param data [in] array of doubles
* #param dataLength [in] number of floats in data
* #param weight [in]
* #param status [in] enum with data status
* #param results [out] array of MaxResults (pre-allocated) DLLResult structs.
* Up to MaxResults results will be returned.
* #param nResults [out] the actual number of results being returned.
*/
void DLLCALL __stdcall analyzeData(
const double *data, int dataLength, float weight, Status status, Result **results, int *nResults);
#ifdef __cplusplus
}
#endif
As used in C#:
private const int DataLength = 10;
private const int StrLen = 16;
private const int MaxThreatPeaks = 30;
public enum Status { on = 0, off = 1 };
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Result
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = StrLen)] public string name; //!< Up to StrLen-1 char null-terminated name
public float location;
public Status status;
}
[DllImport("dllname.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "analyzeData#32")] // "#32" is only used in the 32-bit version.
public static extern void analyzeData(
double[] data,
int dataLength,
float weight,
Status status,
[MarshalAs(UnmanagedType.LPArray, SizeConst = MaxResults)] ref Result[] results,
out int nResults
);
Without the extern "C" part, the C++ compiler would mangle the export name in a compiler dependent way. I noticed that the EntryPoint / Exported function name matches the function name exactly in a 64-bit DLL, but has an appended '#32' (the number may vary) when compiled into a 32-bit DLL. Run dumpbin /exports dllname.dll to find the exported name for sure. In some cases you may also need to use the DLLImport parameter ExactSpelling = true. Note that this function is declared __stdcall. If it were not specified, it would be __cdecl and you'd need CallingConvention.Cdecl.
Here is how it might be used in C#:
Status status = Status.on;
double[] data = { -0.034, -0.05, -0.039, -0.034, -0.057, -0.084, -0.105, -0.146, -0.174, -0.167};
Result[] results = new Result[MaxResults];
int nResults = -1; // just to see that it changes (input value is ignored)
analyzeData(data, DataLength, 1.0f, status, ref results, out nResults);
If you do call native code, make sure your structs are alligned in the memory. CLR does not guarantee alignment unless you push it.
Try
[StructLayout(LayoutKind.Explicit)]
struct DataStruct
{
string data;
int len;
};
More info:
http://www.developerfusion.com/article/84519/mastering-structs-in-c/

Handling Array returned from c++ dll to C#

I have this in my dll created in c++
extern "C" __declspec(dllexport)
char* __stdcall hh()
{
char a[2];
a[0]='a';
a[1]='b';
return(a);
}
And this is how I am trying to handle code in c#
[DllImport(#"mydll.dll",CharSet = CharSet.Ansi,CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr hh();
static void Main(string[] args)
{
IntPtr a = hh();
//How to proceed here???
}
}
Help in proceeding further.
There is no way to handle such arrays. char a[2] is allocated on the stack in your C++ function and is destroyed as soon as you return from it. You should either pass an array from C# and fill it in the C++ code or allocate array in the heap and provide some means for freeing it.
When you have it correct the handling will depend on how you return the data from C++ code. If it's still IntPtr you could use Marshal.ReadByte methods to read characters from memory and use Encoding methods to convert those bytes into string if necessary.
const int bufferSize = 2; // suppose it's some well-known value.
IntPtr p = ...; // get this pointer somehow.
for (var i = 0; i != bufferSize; ++i)
{
var b = Marshal.ReadByte(p, i);
Console.WriteLine(b);
}
I got a solution as follows::
OUR C++ code goes as follows
extern "C" __declspec(dllexport)
char** __stdcall hh()
{
static char* myArray[3] = {"A1", "BB2", "CC3",};
return myArray;
}
And C# goes as follows
[DllImport(#"ourdll.dll",CharSet = CharSet.Ansi,CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr hh();
static void Main(string[] args)
{
IntPtr a = hh();
int j = 0;
string[] s=new string[100];
do
{
s[j] = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(a,4*j));
j++;
}
while(s[j-1] != null);
}
The only problem now faced is that how can we know size of the array
so that in this statement
string[] s=new string[100];
we neednot waste our memory.
The answer would be
string stra = Marshal.PtrToStringAnsi(a);
But you also have the problem that the dll returns garbage per your code as char* is a local c style string.
Would be ok if you would return something like:
const char* str = "Hello from DLL.";
Try to use not empty StringBuilder as the return value.

Categories