I have unmanaged dll in C++ which is working properly, I try to re-implement it by C# but I get following error:
System.ArgumentException: Value does not fall within the expected range
at System.StubHelpers.ObjectMarshaler.ConvertToNative(Object objSrc, IntPtr pDstVariant)
at Demo.on_session_available(Int32 session_id) in C:\\Users\\Peyma\\Source\\Repos\\FastViewerDataClient\\FastViewerDataClient\\Demo.cs:line 69
ExceptionMethod: 8
ConvertToNative
mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.StubHelpers.ObjectMarshaler
Void ConvertToNative(System.Object, IntPtr)
HResult:-2147024809
Source: mscorlib
C++ code is as follow:
typedef void(*func_ptr)(
int sId,
unsigned char cId,
const unsigned char* buf,
int len,
void* context);
struct configuration
{
func_ptr send;
};
struct send_operation
{
int session_id;
unsigned char channel_id;
std::string data;
};
auto op = new send_operation();
op->sId = sessionId;
op->cId = channelId;
op->data = "Some Text";
configuration.send(
sessionId,
channelId,
reinterpret_cast<const unsigned char*>(op->data.c_str()),
op->data.length(),
op);
Then it's translated in C# as follow:
[StructLayout(LayoutKind.Sequential)]
public struct Configuration
{
public Send send { get; set; }
}
[StructLayout(LayoutKind.Sequential)]
public struct send_operation
{
public int session_id { get; set; }
public sbyte channel_id { get; set; }
public string data { get; set; }
};
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void Send(int sessionId, sbyte channelId, sbyte[] buffer, int len, object context);
var op = new send_operation
{
session_id = session_id,
channel_id = 0,
data = "This is a test message!!"
};
var bytes = Encoding.UTF8.GetBytes(op.data);
config.send(sessionId, 0, Array.ConvertAll(bytes, Convert.ToSByte), op.data.Length, op);
UPDATE:
public static void on_session_available(int session_id)
{
WriteOnFile($"Open session id:{session_id}");
try
{
var op = new send_operation
{
session_id = session_id,
channel_id = 0,
data = "This is a test message!!"
};
var bytes = Encoding.UTF8.GetBytes(op.data);
config.send_data(session_id, op.channel_id, bytes, op.data.Length, op);
}
catch (Exception e)
{
WriteOnFile($"Error in sending data:{JsonConvert.SerializeObject(e)}");
if (e.InnerException != null)
{
WriteOnFile($"Error in inner sending data:{e.InnerException.Message}");
}
}
}
Some changes:
C++, std::string to unsigned char*, because it's hard to marshal it in C#.
struct send_operation
{
int session_id;
unsigned char channel_id;
unsigned char* data;
};
C#, object context to IntPtr context, because send_operation is a struct, this will pass the boxed object instead of the struct data.
public delegate void Send(int sessionId, sbyte channelId,
sbyte[] buffer, int len, IntPtr context);
How to pass:
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(Marshal.SizeOf(op));
Marshal.StructureToPtr(op, ptr, false);
config.send_data(session_id, op.channel_id, bytes, op.data.Length, ptr);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
One of the things that has caught me out with this sort of work before is "packing"/word-alignment.
Confirm the exact size of send_operation.
In C# specify equivalent packing of the struct with StructLayout attribute so...
[StructLayout(LayoutKind.Sequential), Pack = 1] // for byte alignment
[StructLayout(LayoutKind.Sequential), Pack = 2] // for word (2 byte) alignment
[StructLayout(LayoutKind.Sequential), Pack = 4] // for native 32 bit (4 byte) alignment
or if necessary explicitly
[StructLayout(LayoutKind.Explicit)]
which requires the FieldOffset attribute on every member
[FieldOffset(0)] // for 1st member
[FieldOffset(4)] // 4 bytes from beginning of struct
When doing integration between C(++) and .Net, it is always a good idea to be explicit about this aspect of your struct types.
Related
Trying to use the UnmanagedCallersOnly attribute but I'm stuck on some string fields in the structure. Is it possible to model this structure that is usable with the new UnmanagedCallersOnly attribute?
C++ structure:
struct PluginInfo {
int nStructSize;
int nType;
int nVersion;
char szName[ 64 ];
char szVendor[ 64 ];
};
I'm getting stuck on converting the szName and szVendor to c#.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct PluginInfo
{
public int StructSize;
[MarshalAs(UnmanagedType.I4)]
public PluginType Type; // PluginType is an enumeration
public int Version;
[MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)] //[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]
public string Name;
[MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)] //[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]
public string Vendor;
}
public class Plugin
{
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
public static void GetPluginInfo(ref PluginInfo pluginInfo)
{
pluginInfo.Name = "myPluginName";
pluginInfo.Vendor = "myVendorName";
pluginInfo.Type = PluginType.Data;
pluginInfo.StructSize = Marshal.SizeOf((PluginInfo)pluginInfo);
}
}
The error is "Cannot use 'PluginInfo' as a parameter type on a method attributed with 'UnmanagedCallersOnly'"
I have been trying to replicate the following C++ function in my C# application but I am getting an AccessViolationException at the time the method "ObjSearchObject2" is invoked. Pretty sure I am doing a lot of things wrong. The C++ function and the corresponding headers looks like this,
bool DocumentSearch(char *pFileId)
{
const int NUM_CONDITIONS = 1;
const int NUM_TYPE_DEFNS = 1;
ObjSearchObjectParm rSearchObject;
ObjSearchCondition rSearchCondition;
ObjObjectResults rSearchResults;
char *pTypeDefnIdArray[NUM_TYPE_DEFNS];
bool bReturnValue = false;
memset(&rSearchObject, 0, sizeof(ObjSearchObjectParm));
memset(&rSearchCondition, 0, sizeof(ObjSearchCondition));
memset(&rSearchResults, 0, sizeof(ObjObjectResults));
pTypeDefnIdArray[0] = "dotdA9";//Documents
rSearchObject.obj_defn_ids = pTypeDefnIdArray;
rSearchObject.obj_defn_count = NUM_TYPE_DEFNS;
rSearchObject.num_conditions = NUM_CONDITIONS;
rSearchObject.max_results = 5000;
rSearchObject.search_cond = &rSearchCondition;
rSearchCondition.field = "ancestor";
rSearchCondition.op = "is";
rSearchCondition.value = pFileId;
if (ObjSearchObject2(&rSearchObject, &rSearchResults))
{
printf("ERROR: ObjSearchObject2 returned: %s \n", ObjGetErrorMsg());
}
else
{
printf("INFO : Number of returned results = %d \n", rSearchResults.num_results);
if (rSearchResults.num_results > 0)
{
for (int iIndex = 0; iIndex < rSearchResults.num_results; iIndex++)
{
if (rSearchResults.object_handle[iIndex])
{
printf("INFO : Object Id returned: %s (name=%s, updated=%s, type=%s, state=%s) \n",
ObjGetObjectAttr(rSearchResults.object_handle[iIndex], "id_object"),
ObjGetObjectAttr(rSearchResults.object_handle[iIndex], "name"),
ObjGetObjectAttr(rSearchResults.object_handle[iIndex], "date_update"),
ObjGetObjectAttr(rSearchResults.object_handle[iIndex], "id_type_definition"),
ObjGetObjectAttr(rSearchResults.object_handle[iIndex], "num_state")
);
ObjFreeObjectHdl(rSearchResults.object_handle[iIndex]);
}
}
bReturnValue = true;
}
}
return bReturnValue;
}
int ObjSearchObject2(ObjSearchObjectParm *, ObjObjectResults *);
char * ObjGetObjectAttr(ObjApiObjectHdl objectHdl, char *attr_name);
char * ObjGetErrorMsg();
void ObjFreeObjectHdl(ObjApiObjectHdl objectHdl);
typedef struct _ObjSearchObjectParm {
int obj_defn_count; // mandatory
char **obj_defn_ids; // mandatory
char *text_server_alias; // optional.
char *query_string; // optional text search string
ObjApiBoolean include_deleted; // defaults to OBJAPI_FALSE
int max_results; // default = 200
int num_conditions; // mandatory
ObjSearchCondition *search_cond; // mandatory
ObjApiBoolean include_content; // mandatory for COMPLEX searches, translated to Y/N in API
ObjApiBoolean include_metadata; // mandatory for COMPLEX searches, translated to Y/N in API
} ObjSearchObjectParm;
enum ObjApiSearchOp
{
OBJAPI_MATCH_ALL=1,
OBJAPI_MATCH_ANY=2
};
enum ObjApiBoolean
{
OBJAPI_FALSE,
OBJAPI_TRUE
};
typedef struct _ObjSearchCondition {
ObjApiSearchOp boolop; // only for complex searches
char *join_relation; // only for complex searches
int num_opening_brackets; // only for complex searches
int num_closing_brackets; // only for complex searches
char *field;
char *op;
char *value;
} ObjSearchCondition;
typedef void* ObjApiObjectHdl;
typedef struct _ObjObjectResults {
ObjApiObjectHdl *object_handle;
int num_results;
} ObjObjectResults;
My take on this is as follows, (UPDATE: Code updated)
public class ObjectiveNativeAPI
{
private const string dllPath = #"objapi.dll";
public enum ObjApiBoolean
{
OBJAPI_FALSE,
OBJAPI_TRUE
};
public enum ObjApiSearchOp
{
OBJAPI_MATCH_ALL = 1,
OBJAPI_MATCH_ANY = 2
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ObjSearchObjectParm
{
public int obj_defn_count;
public string[] obj_defn_ids;
public string text_server_alias;
public string query_string;
public ObjApiBoolean include_deleted;
public int max_results;
public int num_conditions;
public IntPtr search_cond;
public ObjApiBoolean include_content;
public ObjApiBoolean include_metadata;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ObjSearchCondition
{
public ObjApiSearchOp boolop;
public string join_relation;
public int num_opening_brackets;
public int num_closing_brackets;
public string field;
public string op;
public string value;
}
[StructLayout(LayoutKind.Sequential)]
public struct ObjObjectResults
{
public IntPtr object_handle;
public int num_results;
}
[DllImport(dllPath, EntryPoint = "ObjSearchObject2")]
public static extern int ObjSearchObject2(ref ObjSearchObjectParm objSearchObjectParm, ref ObjObjectResults objObjectResults);
[DllImport(dllPath, EntryPoint = "ObjGetObjectAttr")]
public static extern string ObjGetObjectAttr(IntPtr object_handle, string attr_name);
[DllImport(dllPath, EntryPoint = "ObjFreeObjectHdl")]
public static extern void ObjFreeObjectHdl(IntPtr object_handle);
}
public void Run()
{
ObjectiveNativeAPI.ObjSearchObjectParm rSearchObject = new ObjectiveNativeAPI.ObjSearchObjectParm();
ObjectiveNativeAPI.ObjSearchCondition rSearchCondition = new ObjectiveNativeAPI.ObjSearchCondition();
ObjectiveNativeAPI.ObjObjectResults rSearchResults = new ObjectiveNativeAPI.ObjObjectResults();
rSearchCondition.field = "ancestor";
rSearchCondition.op = "is";
rSearchCondition.value = txtCotainerId.Text;
rSearchObject.obj_defn_ids = new[] {"dotdA9"};
rSearchObject.obj_defn_count = 1;
rSearchObject.num_conditions = 1;
rSearchObject.max_results = 5000;
IntPtr search_cond = Marshal.AllocCoTaskMem(Marshal.SizeOf(rSearchCondition));
Marshal.StructureToPtr(rSearchCondition, search_cond, false);
rSearchObject.search_cond = search_cond;
int result = ObjectiveNativeAPI.ObjSearchObject2(ref rSearchObject, ref rSearchResults);
MessageBox.Show(string.Format("FunctionResult: {0}", result));
Marshal.FreeCoTaskMem(search_cond);
}
I am a bit lost on this. I have managed to translate some other segments of this project but this is causing me grief. Any help around this would be appreciated.
Im attempting to marshal a C++ struct containing char[] arrays to C# but on the unmanaged side I see the char arrays as empty and not sure what I'm missing
C# structure
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1), Serializable]
public struct MParams
{
public int action;
public int response;
public long serial_no;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 251)]
public string address; // public char[] address;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 81)]
public string description; // public char[] description;
}
C++ Structure
typedef struct
{
int action;
int response;
LONG serial_no;
char address[251];
char description[81];
}PARAMS;
C++ Exported function
__declspec(dllexport) (int) test_Email(void *params)
{
PARAMS *l_params = (PARAMS *)params;
// params->address // always empty expected abc#abc.com
}
C# Usage
[DllImport(#"test.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint ="?test_Email##YAHPAX#Z")]
public static extern int EMailExists(IntPtr valParams);
MParams mParams = new MParams {
action = 3,
serial_no = 0,
address = abc#abc.com,
description = string.Empty
};
IntPtr pAddr = Marshal.AllocHGlobal(Marshal.SizeOf(mParams));
Marshal.StructureToPtr(mParams, pAddr, false);
EMailExists(pAddr);
mParams = (MParams)Marshal.PtrToStructure(pAddr, typeof(MParams));
I've attempted using char[] to marshall but that too did not help. I reckon I'm missing on something very obvious and help to understand where I'm wrong is much appreciated.
I have a structure like below
[StructLayout(LayoutKind.Sequential)]
public struct MyStructType
{
[MarshalAs(UnmanagedType.U1)]
public byte stx;
public UInt16 cmdId;
public UInt16 status;
public UInt16 pktNo;
[MarshalAs(UnmanagedType.U1)]
public byte contPkt;
[MarshalAs(UnmanagedType.U1)]
public byte dataoffset;
public UInt16 dataLength;
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 6)]
public byte[] data;
public UInt16 checkSum;
[MarshalAs(UnmanagedType.U1)]
public byte cr;
}
i tried to convert this structure to byte array by using below code.
byte[] ConvertStructureToByteArray(MyStructType str)
{
int size = Marshal.SizeOf(str);
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(str, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
but i got the below error while because size they dont know
Type 'MyStructType' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
the problem because of
public UInt16 dataLength;
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 6)]
public byte[] data;
dataLength calculated on runtime. How can i convert this structure to ByteArray?
Limits of Marshalling
As you already noticed, Marshal.SizeOf() cannot compute the size of a struct containing a byte array with UnmanagedType.LPArray. However, it doesn't mean you cannot compute it by yourself.
However, even if you do so, you'll get Marshal.StructureToPtr complaining about data field which must use SafeArray or ByValArray.
You should check this on MSDN in order to understand how to pass arrays from Managed to Unmanaged. However it seems that for arrays contained in a struct:
The size can be set only as a constant
Why not using Serialization ?
Protocol Buffer is a simple way to serialize data to binary. Furthermore, it support model change, model share, and several other cool features.
It is available in several languages:
C# API : Protobuf-csharp-port or Protobuf-net
Google C++ API.
Adding an answer that offers a solution, due to the limits of Marshalling as pointed out in the answer provided by #Fab
public UInt16 dataLength;
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 6)]
public byte[] data;
Marshalling doesn't actually deal with arrays very cleanly because it doesn't calculate the offsets for each element in the array as you would expect (even with the provided attributes, go figure!). Since your structure is small, you can do the following which will marshal the bytes correctly:
[StructLayout(LayoutKind.Sequential)]
public struct MyStructType
{
[MarshalAs(UnmanagedType.U1)]
public byte stx;
public UInt16 cmdId;
public UInt16 status;
public UInt16 pktNo;
[MarshalAs(UnmanagedType.U1)]
public byte contPkt;
[MarshalAs(UnmanagedType.U1)]
public byte dataoffset;
public UInt16 dataLength;
public MyDataArray data;
public UInt16 checkSum;
[MarshalAs(UnmanagedType.U1)]
public byte cr;
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = Size)]
public struct MyDataArray
{
public const int Size = 6;
public byte Byte0;
public byte Byte1;
public byte Byte2;
public byte Byte3;
public byte Byte4;
public byte Byte5;
public MyDataArray(byte[] bytes) {
if (bytes == null || bytes.Length != Size)
throw new ArgumentOutOfRangeException(nameof(bytes));
Byte0 = bytes[0];
Byte1 = bytes[1];
Byte2 = bytes[2];
Byte3 = bytes[3];
Byte4 = bytes[4];
Byte5 = bytes[5];
}
public byte[] ToArray() {
return new byte[Size] { Byte0, Byte1, Byte2, Byte3, Byte4, Byte5 };
}
}
i have C++ structure
struct DressKey
{
int keyId;
GENDER gender;
DRESS_CLASS dressClass;
unsigned int descriptor[KEY_SIZE];
float confidence;
short keyLength;
short metric;
DressKey():
keyId(0),
gender(GENDER_UNDEFINED),
dressClass(CLASS_UNDEFINED),
confidence(0.0f),
keyLength(KEY_SIZE),
metric(0)
{
descriptor[0] = 0;
}
};
Type of GENDER and DRESS_CLASS is enum
And i try pass it in method:
virtual ERROR_CODE ExtractDressKey(
const unsigned char *frame,
int width,
int height,
int lineLength,
int format,
DressKey *dressKey);
I have wrap this method and structure in c# at this way:
[DllImport("DSE.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?ExtractDressKey#DSE#dse##UEAA?AW4ERROR_CODE#2#PEBEHHHHPEAUDressKey#2##Z")]
private static extern ERROR_CODE ExtractDressKey(IntPtr self, IntPtr frame, int width, int height, int lineLength, int format, ref DressKey dressKey);
[StructLayout(LayoutKind.Sequential)]
public class DressKey
{
public const int KEY_SIZE = 5768;
public int keyId; //!< serialization key id
public GENDER gender; //!< gender
public DRESS_CLASS dressClass; //!< dress category
[MarshalAs(UnmanagedType.ByValArray, SizeConst = KEY_SIZE)]
public uint[] descriptor = new uint[KEY_SIZE]; //!< comparable part
public double confidence; //!< key confidence
public short keyLength; //!< actual numer of elements in descriptor
public short metric; //!< ID of a metric revision
}
I got AccessViolationException. What is wrong in my wrapper?
UPD:
usage:
var imgPath = #"C:\1.jpg";
using (var dse = new DSE())
{
using (var img = CV.LoadImageM(imgPath, LoadImageFlags.AnyColor))
{
var subImg = img;
using (subImg)
{
var dk = new DressKey();
var type = subImg.Channels == 1 ? 0 : 1;
var res = dse.ExtractDressKey(subImg.Data, subImg.Cols, subImg.Rows, subImg.Cols, type, ref dk);
if (res == ERROR_CODE.ERROR_OK)
Console.WriteLine("id={0}&key={1}", 123, JsonConvert.SerializeObject(dk));
else
{
Console.WriteLine( res.ToString());
}
}
}
}
You cannot expect to p/invoke C++ instance methods. P/invoke is used to call non-member functions. You need to do one of the following:
Wrap the C++ library in a mixed mode C++/CLI assembly, and have the C# code consume it as a managed reference.
Expose the C++ library using COM.
Wrap the C++ library in a C style procedural interface and access that using p/invoke.