I have a function in C++ that I exported to a DLL. I contains a struct pointer as one of the parameters. I need to use this function in C#, so I used DLLImport for the function and recreated the struct in C# using StructLayout. I've tried passing in the parameter using ref as well as tried Marshaling it in using MarshalAs(UnmangedType.Struct) and Marshal.PtrToStructure. The parameter still isn't passing correctly.
Example:
[DllImport("testdll.dll")]
public static extern int getProduct(int num1, int num2, [MarshalAs(UnmanagedType.Struct)] ref test_packet tester);
One more tidbit of info, the struct contains a byte* var, which I think may be causing the problem in terms of passing the param as ref. Any ideas? Am I on the right track? Thanks for the help.
Thanks nobugz for the response. Here's a sample of the struct def:
//C# DEFINITION
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct test_packet
{
public UInt32 var_alloc_size;
public byte* var;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_TAG_LENGTH)]
public byte[] tag;
}
//ORIGINAL UNMANAGED STRUCT
typedef struct test_packet_tag
{
unsigned int var_alloc_size;
unsigned char *var;
unsigned char tag[MAX_TAG_LENGTH];
} test_packet;
Using "ref" is the correct way, get rid of the [MarshalAs] attribute. The real problem is almost certainly the structure declaration. You didn't post anything that would help us help you with that.
The DllImportAttribute.CharSet property is wrong, make it CharSet.Ansi. The "var" member is declared wrong, make it byte[]. Be sure to initialize it before the call:
var product = new test_packet();
product.var_alloc_size = 666; // Adjust as needed
product.var = new byte[product.var_alloc_size];
int retval = getProduct(42, 43, ref product);
Best guess, hope it works.
Here's an example from my personal stuff. It can be really complicated. Note that its not easy to move arrays over as pointers, so you should look at how one does that in the c# side. This should hit a lot of the major data types. You must make sure your elements line up EXACTLY. Otherwise it will look like its working, but you'll get bad ptrs (at best). I had a great deal of trouble when moving this struct, and this was the only approach that worked. Good luck
Function sig -
[DllImport("stochfitdll.dll", EntryPoint = "Init", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern void Init([MarshalAs(UnmanagedType.LPStruct)] ModelSettings settings);
C++ side
#pragma pack(push, 8)
struct ReflSettings
{
LPCWSTR Directory;
double* Q;
double* Refl;
double* ReflError;
double* QError;
int QPoints;
double SubSLD;
double FilmSLD;
double SupSLD;
int Boxes;
double FilmAbs;
double SubAbs;
double SupAbs;
double Wavelength;
BOOL UseSurfAbs;
double Leftoffset;
double QErr;
BOOL Forcenorm;
double Forcesig;
BOOL Debug;
BOOL XRonly;
int Resolution;
double Totallength;
double FilmLength;
BOOL Impnorm;
int Objectivefunction;
double Paramtemp;
LPCWSTR Title;
};
#pragma pack(pop)
C# side -
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 8)]
public class ModelSettings:IDisposable
{
#region Variables
public string Directory;
public IntPtr Q;
public IntPtr Refl;
public IntPtr ReflError;
public IntPtr QError;
public int QPoints;
public double SubSLD;
public double SurflayerSLD;
public double SupSLD;
public int Boxes;
public double SurflayerAbs;
public double SubAbs;
public double SupAbs;
public double Wavelength;
public bool UseAbs;
public double SupOffset;
public double Percerror;
public bool Forcenorm;
public double Forcesig;
public bool Debug;
public bool ForceXR;
public int Resolution;
public double Totallength;
public double Surflayerlength;
public bool ImpNorm;
public int FitFunc;
public double ParamTemp;
public string version = "0.0.0";
[XmlIgnoreAttribute] private bool disposed = false;
#endregion
public ModelSettings()
{ }
~ModelSettings()
{
Dispose(false);
}
#region Public Methods
public void SetArrays(double[] iQ, double[] iR, double[] iRerr, double[] iQerr)
{
//Blank our arrays if they hold data
if (Q == IntPtr.Zero)
ReleaseMemory();
int size = Marshal.SizeOf(iQ[0]) * iQ.Length;
try
{
QPoints = iQ.Length;
Q = Marshal.AllocHGlobal(size);
Refl = Marshal.AllocHGlobal(size);
ReflError = Marshal.AllocHGlobal(size);
if (iQerr != null)
QError = Marshal.AllocHGlobal(size);
else
QError = IntPtr.Zero;
Marshal.Copy(iQ, 0, Q, iQ.Length);
Marshal.Copy(iR, 0, Refl, iR.Length);
Marshal.Copy(iRerr, 0, ReflError, iRerr.Length);
if (iQerr != null)
Marshal.Copy(iQerr, 0, QError, iQerr.Length);
}
catch (Exception ex)
{
//error handling
}
}
#endregion
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!this.disposed)
{
// Call the appropriate methods to clean up
// unmanaged resources here.
// If disposing is false,
// only the following code is executed.
ReleaseMemory();
// Note disposing has been done.
disposed = true;
}
}
private void ReleaseMemory()
{
if (Q != IntPtr.Zero)
{
Marshal.FreeHGlobal(Q);
Marshal.FreeHGlobal(Refl);
Marshal.FreeHGlobal(ReflError);
if (QError != IntPtr.Zero)
Marshal.FreeHGlobal(QError);
}
}
#endregion
}
Your original P/Invoke declaration should be okay, though you shouldn't need UnmanagedType.Struct there at all. The problem seems to be with your C# struct declaration. In particular, why is the order of the field declarations different from the C++ version?
Related
I'm trying to return a struct from a C++ callback from C# and I get the error as described in the title.
I appreciate there is a lot of information, but I wanted to include too much rather than not enough.
I've tried returning the function as a structure, but then read it may be easier to return the function as an IntPtr and use Marshal.PtrtoStructure.
The function that I want to call is from a .dll that comes from a different source and the input is:
C++:
rVDACQ_Connect(int, tVDACQ_CallBackProc, void*, short*, int)
the function in the sample (C++) code returns a struct (rACQ_CallBackRec) which is as follows:
rACQ_CallBackRec = theApp.m_Intf.rVDACQ_Connect(cVDACQ_FBright, CALLBACK_Acquisition, this, m_FrmBuffer, 0);
//rACQ_CallBackRec is the struct type tVDACQ_CallBackRec (as described below)
With CALLBACK_Aquisition being:
extern "C" {
__declspec(dllexport) void _stdcall CALLBACK_Acquisition(tVDACQ_CallBackRec* AR)
{
tVDACQ_CallBackProc test;
((CPreviewDlg*)AR->rUserParam)->My_ACQ_CallBack(AR, test);
}
}
The layout of the struct is as follows:
typedef struct {
int rFlags, // combination of cVDACQ_Fxxxx
rType, // cVDACQ_ETxxx
rEvent, // cVDACQ_Exxx
rSocket; // 0:no relation to a 'socket'; otherwise socket's ID>0 (event's source ID)
TCHAR rMsg[256]; // message (trace, wrn, err)
int rFrameWidth, // full frame width
rFrameHeight; // full frame height
short* rFrameBuffer; // user supplied frame buffer "AFrameBuffer"
union {
int rCaptureRows; // # of received rows (for single frame acquisition)
int rCaptureFrames; // # of received full frames (for framegrabber)
};
int rCapturePercent; // received data in percents
void* rUserCallBackProc, // user supplied "ACallBackProc"
* rUserParam; // user supplied "AUserParam"
int rAborted; // 1: VDACQ_Abort -1:internally
void* rPacketData; // pointer to received packet; usually it is nil
int rFGControl; // frame-grabber's control flags
} tVDACQ_CallBackRec;
typedef void(_stdcall* tVDACQ_CallBackProc)(tVDACQ_CallBackRec*); //The Callback
C#
I've created the callback procedure in C#, as well as the struct and PInvoke:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void tVDACQ_CallBackProc(tVDACQ_CallBackRec AR);
[DllImport("C:\\Users\\jch\\source\\repos\\FlatPanelSensor\\x64\\Debug\\VADAV_AcqS.dll", EntryPoint = "CALLBACK_Acquisition", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern void CALLBACK_Acquisition(tVDACQ_CallBackRec AR);
[DllImport("C:\\Users\\jch\\source\\repos\\FlatPanelSensor\\FlatPanelSensor\\bin\\Debug\\VADAV_FGM_64.dll", EntryPoint = "VDACQ_Connect", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I4)]
public unsafe static extern IntPtr VDACQ_Connect(int i, [MarshalAs(UnmanagedType.FunctionPtr)] tVDACQ_CallBackProc proc, dynamic n, IntPtr[] buffer, int j);
Struct:
[StructLayout(LayoutKind.Sequential)]
public struct tVDACQ_CallBackRec
{
public int rFlags;
public int rType;
public int rEvent;
public int rSocket;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string rMsg;
public int rFrameWidth;
public int rFrameHeight;
public IntPtr rFrameBuffer;
public int rCaptureRows;
public int rCaptureFrames;
public int rCapturePercent;
public IntPtr rUserCallBackProc;
public IntPtr rUserParam;
public int rAborted;
public IntPtr rPacketData;
public int rFGControl;
}
CallBack:
tVDACQ_CallBackProc callBack =
(AR) =>
{
CALLBACK_Acquisition(AR); //Don't know how necessary this
is
};
Function call:
IntPtr work = VDACQ_Connect(0, callBack, this, m_FrmBuffer, 0);
//I know 'this' isn't defined as a void* in my DLLImport,
//but making it an IntPtr didn't work either so
//the only type I could think of was dynamic.
If I could return this as a IntPtr or, even better, the struct I defined in my C# code that would be great.
If you need anymore information then please let me know, as I think I included everything.
I have a native library and what I'm trying to do is to write a .NET Core wrapper to this native library.
On the native side i have an event called OnSpeechEnded :
virtual void OnSpeechEnded(SpeechEndInfo seInfo) = 0;
and native struct SpeechEndInfo has the following structure:
struct SpeechEndInfo
{
std::vector<AudioData> UntouchedData;
std::vector<AudioData> AudioAfterPostSpeechUntilSilenceTrigger;
};
Corresponding .NET Standard Class:
public struct SpeechEndedInfo
{
public SpeechEndedInfo(short[] untouchedData, short[] audioAfterPostSpeechUntilSilenceTrigger)
{
UntouchedData = new short[untouchedData.Length];
untouchedData.CopyTo(UntouchedData, 0);
AudioAfterPostSpeechUntilSilenceTrigger = new short[audioAfterPostSpeechUntilSilenceTrigger.Length];
audioAfterPostSpeechUntilSilenceTrigger.CopyTo(AudioAfterPostSpeechUntilSilenceTrigger, 0);
}
public short[] UntouchedData { get; set; }
public short[] AudioAfterPostSpeechUntilSilenceTrigger { get; set; }
};
On .NET Standard side, the following delegate is defined:
delegate void OnSpeechEndedInter(
[In, MarshalAs(UnmanagedType.LPArray)] short[] untouched,
int untouchedSize,
[In, MarshalAs(UnmanagedType.LPArray)] short[] audioAfterPostSpeechUntilSilenceTrigger,
int audioAfterPostSpeechUntilSilenceTriggerSize);
Declaring pinvoke:
[DllImport("VadLite.Pinvokable.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void RegisterToSpeechEndedEvent(IntPtr parameters, [MarshalAs(UnmanagedType.FunctionPtr)]OnSpeechEndedInter onSpeechEndedInter);
Declaring _onSpeechEndedInter:
private OnSpeechEndedInter _onSpeechEndedInter;
Assigning a method to the event:
_onSpeechEndedInter = invokedMethod;
...
void invokedMethod(short[] untouchedData,
int untouchedSize,
short[] audioAfterPostSpeechUntilSilenceTrigger,
int audioAfterPostSpeechUntilSilenceTriggerSize)
{
OnSpeechEnded?.Invoke(new SpeechEndedInfo(untouchedData, audioAfterPostSpeechUntilSilenceTrigger));
}
Finally, the code where native arrays are sent:
RegisterToSpeechEndedEvent(
void * possiblyOperations,
void __stdcall onSpeechEndedListener(const int16_t* pUntouchedData, int pUntouchedDataSize, const int16_t *vAudioAfterPostSpeechUntilSilenceTrigger, int vAudioAfterPostSpeechUntilSilenceTriggerSize))
{
auto op = (OperationParameters *)possiblyOperations;
op->OnSpeechEnded([onSpeechEndedListener](SpeechEndInfo ssInfo) {
std::vector<int16_t> untouchedData;
std::vector<int16_t> audioAfterPostSpeechUntilSilenceTrigger;
for (auto & audioData : ssInfo.UntouchedData)
{
untouchedData.insert(untouchedData.end(), audioData.Samples, audioData.Samples + audioData.SampleCount);
}
for (auto & audioData : ssInfo.AudioAfterPostSpeechUntilSilenceTrigger)
{
audioAfterPostSpeechUntilSilenceTrigger.insert(audioAfterPostSpeechUntilSilenceTrigger.end(), audioData.Samples, audioData.Samples + audioData.SampleCount);
}
onSpeechEndedListener(untouchedData.empty() ? nullptr : untouchedData.data(),
(int)untouchedData.size(),
audioAfterPostSpeechUntilSilenceTrigger.empty() ? nullptr : audioAfterPostSpeechUntilSilenceTrigger.data(),
(int)audioAfterPostSpeechUntilSilenceTrigger.size());
});
After running the test program and getting meaningful array sizes on the native side, C# arrays UntouchedData and AudioAfterPostSpeechUntilSilenceTrigger always seem to have size 1.
Probably there is a detail I have missed. I can provide further detail if it is required. Thanks.
Great!, Finally I have found out the problem. In the delegate declaration, I just had to introduce parameter SizeParamIndex :
delegate void OnSpeechEndedInter(
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] short[] untouched,
int untouchedSize,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] short[] audioAfterPostSpeechUntilSilenceTrigger,
int audioAfterPostSpeechUntilSilenceTriggerSize);
Because the marshaler cannot determine the size of an unmanaged array, we have to pass it in as a separate parameter considering the signature of the method and zero based indexing.
I have a DLL developed in C++ that performs some computations. I am trying to link this DLL into my C# application.
I have a struct within a struct which contains a char array (C string). I have a pointer to the object created by the C++ DLL within my C# application.
namespace Test
[StructLayout(LayoutKind.Sequential)]
public unsafe struct Child
{
public float number1;
public float number2;
[MarshalAs(UnmanagedType.LPArray)]
public char[] name;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct DataStructure
{
public Child child;
...
}
.
.
.
public unsafe partial class Form1:Form {
[DllImport("Calculation.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern IntPtr createInstance();
[DllImport("Calculation.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern DataStructure* processData(IntPtr source); // Setup of data
[DllImport("Calculation.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern int calculate(IntPtr calc); // Perform calculation
.
.
public Form2()
{
private void button1_Click_1(object sender, EventArgs e)
{
// test functions
unsafe
{
IntPtr ptr1 = createInstance(); // Returns pointer to the instance
DataStructure* data = processData(ptr1); // Returns pointer to DataStructure object created through DllImport
data->child.number1 = 1.234F; // Works
data->child.number1 = 9.876F; // Works
data->child.name = "......" // This doesn't work!!
int result = calculate(ptr1); // Returns value when name commented
}
}
}
}
If I don't comment name, I get the Error - Cannot take the address of managed object.
If I comment name, I am able to run the calculation without issue.
I wish to update the value of the char array name but can't seem to figure out the solution. Below is my usual C solution. Thanks!
#include "DataStructure.h"
#define DLLNAME "Calculation.dll"
int main(int argc, char** argv) {
HINSTANCE dllHandle = LoadLibraryA(fileNameDll);
const void* ptr1 = 0;
int result = 0;
DataStructure *data;
ptr1 = createInstance();
data = processData(ptr1);
data->child.number1 = 1.234;
data->child.number2 = 9.876;
sprintf(data->child.name, "3104-03");
result = calculate(ptr1);
}
I'm trying to hook the QPainter::drawText function in another application by injecting my DLL and detour the QPainter::drawText function to my own application. I'm doing this because the other application does not expose a usable API and I want to do some basic statistical analysis on the data I'm getting.
All is working fine: I see the QPainter::drawText functions being called, but I'm unable to convert the QString parameter to anything useful. All I get is two characters when I Marshall the QString parameter as LPWStr.
I'm no C++ superhero, so I'm a bit lost. I think I'm looking at some pointer or reference because of the two characters I get for each call, but I'm not sure. After a few nights trying to make sense of it I'm close to the point of giving up.
I've demangled the QPainter::drawText function (found using Dependency Walker: ?drawText#QPainter##QAEXABVQRect##HABVQString##PAV2##Z) with https://demangler.com/ and it comes up with this function declaration:
public: void __thiscall QPainter::drawText(class QRect const &,int,class QString const &,class QRect *)
I've converted this into the following DllImport (I substituted the QRect and Qstring classes to IntPtr, because I have no idea how to convert them to C#).
[DllImport("Qt5Gui.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.ThisCall, EntryPoint = "?drawText#QPainter##QAEXABVQRect##HABVQString##PAV2##Z")]
public static extern void QPainter_drawText(IntPtr obj, IntPtr p1, int p2, IntPtr p3, IntPtr p4);
This is what I have so far:
Detouring Qt QPainter::drawText
LocalHook QPainter_drawTextHook;
[DllImport("Qt5Gui.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.ThisCall, EntryPoint = "?drawText#QPainter##QAEXABVQRect##HABVQString##PAV2##Z")]
public static extern void QPainter_drawText(IntPtr obj, IntPtr p1, int p2, IntPtr p3, IntPtr p4);
[UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Unicode, SetLastError = true)]
delegate void TQPainter_drawText(IntPtr obj, IntPtr p1, int p2, IntPtr p3, IntPtr p4);
static void QPainter_drawText_Hooked(IntPtr obj, IntPtr p1, int p2, IntPtr p3, IntPtr p4)
{
var qs3 = (QString)Marshal.PtrToStructure(p3, typeof(QString));
try
{
((Main)HookRuntimeInfo.Callback).Interface.GotQPainter_drawText(qs3.ToString());
QPainter_drawText(obj, p1, p2, p3, p4);
}
catch (Exception ex)
{
((Main)HookRuntimeInfo.Callback).Interface.ErrorHandler(ex);
}
}
Create QPainter::drawText detour
QPainter_drawTextHook = LocalHook.Create(
LocalHook.GetProcAddress("Qt5Gui.dll", "?drawText#QPainter##QAEXABVQRect##HABVQString##PAV2##Z"),
new TQPainter_drawText(QPainter_drawText_Hooked),
this);
QPainter_drawTextHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
Update 2016-1-31
Thus far I have found this (see https://github.com/mono/cxxi/blob/master/examples/qt/src/QString.cs). But now I'm getting an AccessViolationException on the Marshal.PtrToStringUni.
[StructLayout(LayoutKind.Sequential)]
public unsafe struct QString
{
[StructLayout(LayoutKind.Sequential)]
public struct Data
{
public int #ref;
public int alloc, size;
public IntPtr data;
public ushort clean;
public ushort simpletext;
public ushort righttoleft;
public ushort asciiCache;
public ushort capacity;
public ushort reserved;
public IntPtr array;
}
public Data* d;
#endregion
public override string ToString()
{
try
{
return Marshal.PtrToStringUni(d->array, d->alloc * 2);
}
catch (Exception ex)
{
return ex.Message;
}
}
}
QString is the complex type.
You can try to import QString::FromUtf16 for create QString and pass IntPtr to your function
string str = "Test";
var p2 = new IntPtr(QString.Utf16(str,str.Length));
EDIT: Also, you can try https://github.com/ddobrev/QtSharp for this
Thanks to the helpful article https://woboq.com/blog/qstringliteral.html, which explains the QString data structure in Qt5 I've managed to get the hook working:
[StructLayout(LayoutKind.Sequential)]
public unsafe struct QString
{
[StructLayout(LayoutKind.Sequential)]
public struct QStringData
{
public int #ref;
public int size;
public uint alloc;
public uint capacityReserved;
public fixed byte data[128];
}
public QStringData* data;
public override string ToString()
{
try
{
var bytes = new byte[data->size * 2];
Marshal.Copy((IntPtr)data->data, bytes, 0, data->size * 2);
return Encoding.Unicode.GetString(bytes);
}
catch (Exception ex)
{
return ex.Message;
}
}
}
I'm trying to get back this struct from a native C library through P/Invoke:
struct ndb_mgm_cluster_state
{
int no_of_nodes;
struct ndb_mgm_node_state node_states[1];
};
where ndb_mgm_node_state is:
struct ndb_mgm_node_state {
int node_id;
enum ndb_mgm_node_type node_type;
enum ndb_mgm_node_status node_status;
int start_phase;
int dynamic_id;
int node_group;
int version;
int connect_count;
char connect_address[sizeof("000.000.000.000")+1 ];
int mysql_version;
};
The method's signature is:
ndb_mgm_cluster_state* WINAPI wrap_ndb_mgm_get_status(HANDLE handle);
All of this is provided by a 3rd party library, so nothing can be changed.
In C# i have the follow definitions:
[DllImport("Ndb_CWrapper.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr wrap_ndb_mgm_get_status(IntPtr handle);
the structures are:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ndb_mgm_cluster_state {
public int no_of_nodes;
public IntPtr node_states;
};
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ndb_mgm_node_state
{
public int node_id;
public ndb_mgm_node_type node_type;
public ndb_mgm_node_status node_status;
public int start_phase;
public int dynamic_id;
public int node_group;
public int version;
public int connect_count;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 17)]
public string connect_address;
public int mysql_version;
};
I tried to unmarshal the results without success (i recieve an oddly error (not an Exception) Fatal error execution, can be a CLR bug or a miscall P/invoke.
Obviously the reason is a problem in my P/Invoke call
I tried in this way:
First of all i unmarshalled the ndb_mgm_cluster_state structure:
var res=(ndb_mgm_cluster_state)Marshal.PtrToStructure(
tmpPtr, typeof(ndb_mgm_cluster_state));
where IntPtr is the result of the native call.
Up to this step everything "seems" to be done right, but when i try to unmarshall the node_states i get the error:
ndb_mgm_node_state tmpNode = (ndb_mgm_node_state)Marshal.PtrToStructure(
status.node_states, typeof(ndb_mgm_node_state));
What can be the problem? I supposed is something related to the strange declaration of ndb_mgm_cluster_state because is defined an array of 1 element, but it contain several elements. (the number of elements is in no_of_nodes)
WORKAROUND:
The only way to let everything work i found is to change a bit the signature, in this way:
ndb_mgm_node_state* WINAPI wrap_ndb_mgm_get_status(HANDLE handle,int* length);
in C#:
[DllImport("Ndb_CWrapper.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr wrap_ndb_mgm_get_status(IntPtr handle,out int length);
where length contains no_of_nodes
the unmarshall will be in this way:
IntPtr tmpPtr = wrap_ndb_mgm_get_status(raw,out length);
ndb_mgm_cluster_state tmpRes = new ndb_mgm_cluster_state();
tmpRes.no_of_nodes = length;
tmpRes.node_states = new ndb_mgm_node_state[length];
int step=0;
for (int i = 0; i < tmpRes.no_of_nodes; i++)
{
tmpRes.node_states[i] = (ndb_mgm_node_state)Marshal.PtrToStructure(
tmpPtr+(step*i), typeof(ndb_mgm_node_state));
step = Marshal.SizeOf(tmpRes.node_states[i]);
}
I know the step calculation is odd at the monent, but its not that the point.
there is no way to let the thing work returning directly the ndb_mgm_cluster_state struct instead of do all of this?