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.
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 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.
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.
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.
I'm trying to use CredWrite, but get an ERROR_INVALID_PARAMETER 87 (0x57) error. The intent is to have a secure place to save the user's password for my .net WPF application.
And my code:
public class CredMan
{
private const string TARGET_PREFIX = "myappname:";
public static void SavePassword(string username, string password)
{
Win32CredMan.Credential cred = new Win32CredMan.Credential();
cred.Flags = 0;
cred.Type = Win32CredMan.CRED_TYPE.GENERIC;
cred.TargetName = TARGET_PREFIX + username;
var encoding = new System.Text.UTF8Encoding();
cred.CredentialBlob = encoding.GetBytes(password);
cred.Persist = Win32CredMan.CRED_PERSIST.LOCAL_MACHINE;
cred.UserName = username;
bool isGood = Win32CredMan.CredWrite(cred, 0);
int lastError = Marshal.GetLastWin32Error();
}
}
This is the win32 wrapper: (mostly grabbed from pinvoke.net)
internal class Win32CredMan
{
[DllImport("Advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CredRead(string target, CRED_TYPE type, int reservedFlag,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CredentialInMarshaler))]out Credential credential);
[DllImport("Advapi32.dll", EntryPoint = "CredFreeW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern void CredFree(IntPtr buffer);
[DllImport("Advapi32.dll", SetLastError = true, EntryPoint = "CredWriteW", CharSet = CharSet.Unicode)]
public static extern bool CredWrite([In] Credential userCredential, [In] UInt32 flags);
public enum CRED_TYPE : uint
{
GENERIC = 1,
DOMAIN_PASSWORD = 2,
DOMAIN_CERTIFICATE = 3,
DOMAIN_VISIBLE_PASSWORD = 4,
GENERIC_CERTIFICATE = 5,
DOMAIN_EXTENDED = 6,
MAXIMUM = 7, // Maximum supported cred type
MAXIMUM_EX = (MAXIMUM + 1000), // Allow new applications to run on old OSes
}
public enum CRED_PERSIST : uint
{
SESSION = 1,
LOCAL_MACHINE = 2,
ENTERPRISE = 3,
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CREDENTIAL_ATTRIBUTE
{
string Keyword;
uint Flags;
uint ValueSize;
IntPtr Value;
}
//This type is deliberately not designed to be marshalled.
public class Credential
{
public UInt32 Flags;
public CRED_TYPE Type;
public string TargetName;
public string Comment;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
public byte[] CredentialBlob;
public CRED_PERSIST Persist;
public CREDENTIAL_ATTRIBUTE[] Attributes;
public string TargetAlias;
public string UserName;
}
}
I ran into this same problem now.
I found that this issue occurred using the DOMAIN_PASSWORD option as credential type.
It turns out the TargetName contained an incorrect value.
you should only specify the dns or ip address (wildcard optional),
but NOT containing a full url or protocol.
e.g. "*.microsoft.com" is correct, but "http://www.microsoft.com/" is INVALID
I'll just post this here in case other people run into this issue. took me a while to find it.