Best Way To Marshal A Pointer of Array of Struct - c#

I'm calling functions from C++ that returns a pointer to an array of struct and I'm having problems since I'm new to this operation/implementation.
My C++ codes:
// My C++ Structs
typedef struct _MainData {
double dCount;
DataS1 *DS1;
int iCount1;
DataS2 *DS2;
int iCount2;
}MainData;
typedef struct _DataS1 {
unsigned int uiCount1;
unsigned int uiCount2;
int iCount;
void *pA;
void *pB;
} DataS1;
typedef struct _DataS2 {
unsigned int uiCount1;
unsigned int uiCount2;
unsigned int uiCount3;
unsigned int uiCount4;
double dCount;
int iCount1;
char strLbl[64];
} DataS2;
// My C++ Function
MainData* GetData(const int ID)
{
MainData* mData;
int iLength = Get_Count();
mData = new MainData[iLength];
for(int x = 0;x < VarCounter; x++)
{
// Codes here assign data to mData[x]
}
return mData;
}
Question:
How can I call the C++ function GetData to C#?
My current codes in C# are:
[DllImport(".\\sdata.dll")]
[return: MarshalAs(UnmanagedType.LPArray)]
private static unsafe extern MainData[] GetData(int ID);
// The struct MainData in my C# side is already "Marshalled"...
//My function call is here:
MainData[] SmpMapData = GetData(ID);
When I compiled it, there's an exception:
"Cannot marshal 'return value': Invalid managed/unmanaged type combination."
Sorry for the poor coding... Please help...

I tried to do exactly the same thing but didn't succeed due to lack of time but I learned something in process:
Allocate memory in C#
To pass array of structs, struct must be blittable.
Good luck with that, I couldn't make it to work.

One problem is that the marshaller doesn't know how many items are in the array returned by the C++ code. An alternative approach could be to have two C++ methods - one which returns the number of items, and one which returns a single MainData given an index.
What do your structures look like on the C# side?
Since you are coding both the C++ and C# side, it may be easier to use C++/CLI to interface them instead of PInvoke.

Related

Remove out parameters from C# wrapper of C++ function in SWIG

I am wrapping a C++ object and it has certain methods that return by value, and I would like the C# wrapper method to return a native type without having to allocate and de-allocate memory in C++-land. In this situation I think copying data into the return type is appropriate but I can't figure out how to do it in a nice way.
As a simple working example the following C++ code is a file parser. I only list the the std::pair<int,int> version() method but you can imagine other that return a small constant std::array, list, etc.
#include <utility>
#include <tuple>
class MyFile {
public:
std::pair<int,int> version() const noexcept {
return {version_major_, version_minor_};
}
private:
int version_major_;
int version_minor_;
};
Using Out Arguments
I like this one the best, but I can't for the life of me figure out how to remove the out arguments for just the C# wrapper! C# is responsible for the memory and just passes it to C++ to fill in. This requires some C++ wrappers using %extend but that is ok.
I really want a final C# method with signature of public Tuple<int, int> version() 
SWIG code
%module parser
%include "typemaps.i"
%rename("$ignore", fullname=1) MyFile::version() const noexcept;
%extend MyFile {
void version(int* major_out, int* minor_out) {
std::tie(*major_out, *minor_out) = self->version();
}
}
%typemap(ctype, out="void *") int* major_out, int* minor_out "int *"
%typemap(imtype, out="void") int* major_out, int* minor_out "out int"
%typemap(cstype) int* major_out, int* minor_out "out int"
%typemap(cstype) void MyFile::version "Tuple<int, int>"
%typemap(csin, pre=" int tmp_$csinput = 0;") int* major_out, int* minor_out "out tmp_$csinput"
// implment the C# function that returns the type specified above
%typemap(csout, noblock=1, excode=SWIGEXCODE) void MyFile::version
{
$imcall;$excode
major_out = tmp_major_out;
minor_out = tmp_minor_out;
return new Tuple<int, int>(tmp_major_out, tmp_minor_out);
}
Generated C++ wrapper
SWIGEXPORT void SWIGSTDCALL CSharp_parser_MyFile_version(void * jarg1, int * jarg2, int * jarg3) {
MyFile *arg1 = (MyFile *) 0 ;
int *arg2 = (int *) 0 ;
int *arg3 = (int *) 0 ;
arg1 = (MyFile *)jarg1;
arg2 = (int *)jarg2;
arg3 = (int *)jarg3;
MyFile_version(arg1,arg2,arg3);
}
Generated PInvoke Code
[global::System.Runtime.InteropServices.DllImport("parser", EntryPoint="CSharp_parser_MyFile_version")]
public static extern void MyFile_version(global::System.Runtime.InteropServices.HandleRef jarg1, out int jarg2, out int jarg3);
Generated C# Bindings
public Tuple<int, int> version(out int major_out, out int minor_out) {
int tmp_major_out = 0;
int tmp_minor_out = 0;
parserPINVOKE.MyFile_version(swigCPtr, out tmp_major_out, out tmp_minor_out);
major_out = tmp_major_out;
minor_out = tmp_minor_out;
return new Tuple<int, int>(tmp_major_out, tmp_minor_out);
}
Other methods
I have tried other methods but to keep the question short I won't post all the generated code here.
Returning pointer to a static array
this works, but requires static data for every method that I want to work this way. This doesn't scale. I could easily run into lifetime issues if I try to do this with member data that I assume will live long enough for C# to copy.
wrap the default generated code in a csout typemap
This also works but allocated and de-allocates a lot for a function just returning two ints! you end up with a C# wrapper something like:
public Tuple<int, int> version() {
System.IntPtr cPtr = parserPINVOKE.MyFile_version(swigCPtr);
pair_of_int tmp = new pair_of_int(cPtr, false);
Tuple<int, int> ret = new Tuple<int, int>(tmp.first, tmp.second);
tmp.Dispose();
return ret;
}
Other SO Questions
a similar question is this one, but that deals with java. Java has JNI and Pyhton has a C interface that lets you create memory/object in the C code and return it... I don't see a way to do that in .NET withou C++/CLI (which SWIG isn't using).

How to call an ActiveX function with char * parameter in C#

I have this c++ function :
unsigned int ReadUserMemory( unsigned char *lpBuffer, unsigned int iAddress, unsigned int nNumberOfByte );
Which reads nNumberOfByte bytes from iAddress of a memory and places it in lpBuffer.
I created an ATL Object for it( Going to be used in C# ) with this interface :
[id(6), helpstring("method ReadUserMemory")] HRESULT ReadUserMemory(BYTE* lpBuffer, [in] ULONG iAddress, [in] ULONG nNumberOfByte, [out,retval] ULONG* result);
I'm neither C# programmer nor experienced ATL programmer! Now I'm going to test this function in C# with this code :
byte [] b = new byte[100];
axObj.ReadUserMemory(b, 0, 100);
But apparently this code is wrong. How to call this method? Thanks in advance.
I think Peter R has a very valid point, in that using COM is complicated, because it requires learning COM as well as C++ and C#, as you've discovered. C++ interop is another option, but that also requires learning a third technology, called C++/CLI, which is probably a better fit than COM, but can still be problematic. Both COM and C++/CLI are quite big, complicated beasts, so you might be better off just declaring the C function you need in a dll and using P/Invoke, see e.g. http://msdn.microsoft.com/en-us/magazine/cc164123.aspx. If so, the magic you want can be found in How can I pass a pointer to an array using p/invoke in C#?.
Assuming you want to continue down the COM route, I had a play around with it, and to me it looks like you need to get something that is going to work via the type library that is embedded in the COM dll. In short, I couldn't make the COM interop work with unsigned char *lpBuffer, although perhaps someone else can! The type-library friendly type to use is a SAFEARRAY, which is essentially the way that VB likes to see arrays.
In this case, your idl becomes
[id(1)] HRESULT ReadUserMemory([out] SAFEARRAY(byte)* buffer, [in] ULONG iAddress, [in] ULONG nNumberOfByte, [out,retval] ULONG* result);
Your C++ implementation looks like something like this
STDMETHODIMP CObj::ReadUserMemory(SAFEARRAY ** pBuffer, ULONG iAddress, ULONG nNumberOfByte, ULONG* result)
{
if (pBuffer== nullptr)
return E_POINTER;
SAFEARRAY*& psa = *pBuffer;
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = nNumberOfByte;
psa = SafeArrayCreate(VT_UI1, 1, rgsabound);
if(psa == NULL)
return E_OUTOFMEMORY;
void* data = nullptr;
SafeArrayAccessData(psa, &data);
for(int i=0; i<nNumberOfByte; ++i) {
((char*)data)[i] = (char)i;
}
SafeArrayUnaccessData(psa);
return S_OK;
}
The SafeArrayUnaccessData should be called even if the code after the SafeArrayAccessData fails.
The C# client now looks like this, notably we have changed from byte[] to System.Array
static void Main(string[] args)
{
RUMLib.IObj axObj = new RUMLib.Obj();
Array a = null;
axObj.ReadUserMemory(out a, 2, 6);
for (int i = 0; i < a.Length; ++i)
{
Console.Write("{0},", a.GetValue(i));
}
Console.WriteLine();
}
and the output of the program is
0,1,2,3,4,5,
Note that there will v likely be marshalling, i.e. copying of the data, as the data comes back from the SAFEARRAY to the C# array. This might be prohibitively costly for you, in which case suggest you go with C++/CLI or P/Invoke.

Using Delphi's stuct arrays and strings in C#

I've been trying to invoke a method that have been created in Delphi in the following way:
function _Func1(arrParams: array of TParams): Integer;stdcall;
type
TParams = record
Type: int;
Name: string;
Amount : Real;
end;
My code is:
[DllImport("some.dll", EntryPoint = "_Func1", CallingConvention = CallingConvention.StdCall)]
public static extern int Func(
[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.Struct)] TParams[] arrParams)
And the struct is:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TParams
{
public int Type;
[MarshalAs(UnmanagedType.AnsiBStr)]
public string Name;
public double Amount;
}
When I am calling this method I'm getting the error:
Cannot marshal field 'Name' of type 'TParams': Invalid managed/unmanaged type combination (String fields must be paired with LPStr, LPWStr, BStr or ByValTStr).
However none of those combinations works, as Delphi's strings are prefixed with its length and it is Ansi for sure (I've tried it with other string parameters). Does anyone have a clue how to solve this?
There are two main problems with this, use of open arrays and use of Delphi string.
Open arrays
Delphi open arrays are implemented by passing a pointer to the first element of the array and an extra parameter specifying the index of the last item, high in Delphi terminology. For more information see this answer.
Delphi strings
The C# marshaller cannot interop with a Delphi string. Delphi strings are private types, only to be used internally to a Delphi module. Instead you should use a null-terminated string, PAnsiChar.
Putting it all together you can write it like this:
Delphi
type
TParams = record
_Type: Integer;//Type is a reserved word in Delphi
Name: PAnsiChar;
Amount: Double;
end;
function Func(const arrParams: array of TParams): Integer; stdcall;
C#
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct TParams
{
public int Type;
public string Name;
public double Amount;
}
[DllImport("some.dll")]
public static extern int Func(TParams[] arrParams, int high);
TParams[] params = new TParams[len];
...populate params
int retval = Func(params, params.Length-1);
To compliment David's answer, you can marshal to a Delphi string, but it's ugly. In C#, you have to replace all of your strings in the struct with IntPtr.
private static IntPtr AllocDelphiString(string str)
{
byte[] unicodeData = Encoding.Unicode.GetBytes(str);
int bufferSize = unicodeData.Length + 6;
IntPtr hMem = Marshal.AllocHGlobal(bufferSize);
Marshal.WriteInt32(hMem, 0, unicodeData.Length); // prepended length value
for (int i = 0; i < unicodeData.Length; i++)
Marshal.WriteByte(hMem, i + 4, unicodeData[i]);
Marshal.WriteInt16(hMem, bufferSize - 2, 0); // null-terminate
return new IntPtr(hMem.ToInt64() + 4);
}
This can directly be sent to Delphi, where it'll properly be read as a string.
Remember that you must free this string when you're done with it. However, GlobalFree() can't be called directly on the pointer to the string, because it doesn't point to the start of the allocation. You'll have to cast that pointer to a long, then subtract 4, then cast it back to a pointer. This compensates for the length prefix.

Improper marshaling: C# array to a C++ unmanaged array

I have the following C# code with a structure definition (CInput), obj definition and init, and a call to a C++ (native) DLL function (that has also been written by me).
//C# code
public struct CInput
{
[MarshalAsAttribute(UnmanagedType.R8)]
public double Time;
[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_R8)]
public double[] Database;
/* other similar fields*/
}
CInput Inputs = new CInput();
/* init of Inputs fields*/
int bfr = Example(ref Inputs); //'Example' being the C++ DLL call
Messagebox.Show(bfr.ToString());
There is an error in the marshaling of the second parameter, I don't know where. Then:
//C++ code
struct CInput {
double Time;
double Database[3650];
/*etc*/
}
int Example(CInput& ObjIn) {
return ObjIn.Database[0]; // just an example
}
If I'm not careful and specify only "SafeArray" in the Database marshaling I get a 'error in reading/writing memory, probably corrupt' etc.
if "Database" was marshaled as ByValArray everything is fine, the values show up correctly. Unfortunately I get an internal size exception because I have many arrays of that size, therefore I have to go for pointers - but anything with "SizeArray" will bring the following results (with the code just posted):
(from C++):
Database[0] = **0**
Database[1..etc] = values of the next parameters in the struct marshaled with ByValArray.
I think I should mention that I need the same identical structure from C# to C++, I'm not looking for anything fancy. So Array in a Struct >>> Array in a Struct.
ANY insight in reference to this would be of great value. I have been looking for hours and I don't have yet a solution.
Many thanks in advance.
As I understand your question, you can't use ByValArray with SizeConst because your real struct has a large number of such arrays which results in the stack overflowing.
You opine that maybe you need to use pointers in your structs and I agree with you. Here is how to do it.
On the C++ side you should declare each array as a pointer to the element type:
struct CInput {
double *array;
}
You may also wish to include the length of the arrays in the struct to avoid excessive amounts of hard-coded constants.
All the hard work happens on the C# side.
public struct CInput
{
public IntPtr array;
}
...
double[] theArray = new double[3650];
CInput input = new CInput();
input.array = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(double))*theArray.Length);
try
{
Marshal.Copy(theArray, 0, input.array, theArray.Length);
//call your C++ function here
}
finally
{
Marshal.FreeHGlobal(input.array);
}
public struct CInput
{
public double Time;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3650)]
public double[] Database;
}
CInput Inputs = new CInput();
int bfr = Example(ref Inputs);
Edit. If you need to have dynamically allocated Database array, both C++ and C# code should be changed. In C++ Database should be defined as double*, and you need to add array length somewhere. In C# Database should be declared as IntPtr, and allocated using Marshal.AllocHGlobal method. Please correct C++ structure according to your requirements, and then C# code can be fixed according to this.

Passing pointers from unmanaged code

I have a C# project that imports a C dll, the dll has this function:
int primary_read_serial(int handle, int *return_code, int *serial, int length);
I want to get access to the serial parameter. I've actually got it to return one character of the serial parameter, but I'm not really sure what I'm doing and would like to understand what is going, and of course get it working.
So, I'm very sure that the dll is being accessed, other functions without pointers work fine. How do I handle pointers? Do I have to marshal it? Maybe I have to have a fixed place to put the data it?
An explanation would be great.
Thanks!
Richard
You will have to use an IntPtr and Marshal that IntPtr into whatever C# structure you want to put it in. In your case you will have to marshal it to an int[].
This is done in several steps:
Allocate an unmanaged handle
Call the function with the unamanged array
Convert the array to managed byte array
Convert byte array to int array
Release unmanaged handle
That code should give you an idea:
// The import declaration
[DllImport("Library.dll")]
public static extern int primary_read_serial(int, ref int, IntPtr, int) ;
// Allocate unmanaged buffer
IntPtr serial_ptr = Marshal.AllocHGlobal(length * sizeof(int));
try
{
// Call unmanaged function
int return_code;
int result = primary_read_serial(handle, ref return_code, serial_ptr, length);
// Safely marshal unmanaged buffer to byte[]
byte[] bytes = new byte[length * sizeof(int)];
Marshal.Copy(serial_ptr, bytes, 0, length);
// Converter to int[]
int[] ints = new int[length];
for (int i = 0; i < length; i++)
{
ints[i] = BitConverter.ToInt32(bytes, i * sizeof(int));
}
}
finally
{
Marshal.FreeHGlobal(serial_ptr);
}
Don't forget the try-finally, or you will risk leaking the unmanaged handle.
If I understand what you're trying to do, this should work for you.
unsafe
{
int length = 1024; // Length of data to read.
fixed (byte* data = new byte[length]) // Pins array to stack so a pointer can be retrieved.
{
int returnCode;
int retValue = primary_read_serial(0, &returnCode, data, length);
// At this point, `data` should contain all the read data.
}
}
JaredPar gives you the easy way to do it however, which is just to change your external function declaration so that C# does the marshalling for you in the background.
Hopefully this gives you an idea of what's happening at a slightly lower level anyway.
When writing your P/invoke declaration of that function, try using keyword ref for the pointer parameters like this:
[DllImport("YouDll.dll", EntryPoint = "primary_read_serial")]
public static extern int primary_read_serial(int, ref int, ref int, int) ;
I'm not sure if you need to specify the parameters' name in C#. And remember, when calling that method, you will also have to use ref in the arguments you're passing by reference.

Categories

Resources