I am trying to marshal data from C# to C++ DLL. For which, I have following C++ code.
// C++ Function in DLL
int __declspec(dllexport) DataAnalyze(AllInfo *&all)
{
return 0;
}
// Structure
typedef struct
{
char model[100];
char log[_MAX_PATH];
char path[_MAX_PATH];
}AllInfo;
Here is my C# code.
public unsafe void Execute()
{
// Assigning values
public AllInfo ana_alls = new AllInfo();
AllInfo adt = new AllInfo(0);
adt.Eye_img_num = 5;
adt.Flow_log = #"D:\\HTML\\File.txt";
adt.Pano_path = #"D:\\HTML\\Img.bmp";
ana_alls = adt;
// Preparing data for marshalling
IntPtr intPtrsAll = new IntPtr();
intPtrsAll = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(AllInfo)));
Marshal.StructureToPtr<AllInfo>(ana_alls, intPtrsAll, false);
// DLL calling
int ret = 0 - DataAnalyze(ref intPtrsAll);
}
// Structure
[StructLayout(LayoutKind.Sequential)]
public struct AllInfo
{
public AllInfo(int n)
{
this.model = string.Empty;
this.path = string.Empty;
this.log = string.Empty;
}
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string model;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string log;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string path;
}
// DLL Calling
[DllImport(#"D:\\HTML\\Data\\DLL\\Dara_Dll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int DataAnalyze(ref IntPtr ana_all);
In the C++ method, I am getting data in path of AllInfo *&all as follow:
path :{'\0','\0','\0','\0','\0','\0','\0','\0','D',':','\\','H','T','M','L','\\','I','m','g','.','b','m','p','\0','\0',........}
I am getting similar output for log also. I don't know why '\0' added from 0 to 7 index of each char[] data in C++ that is coming from C#. Where am I doing it wrong?
Related
A C dll has:
struct CredPair{
char usr[127];
char pas[127];
};
struct CredData{
enum CredType {
PAIR,
KEY
} credType;
void* CredVal;
};
struct EndpointData {
enum EndpointType {
DIRECT
} endpointType;
void* endpointVal;
};
struct EndpointDirect {
char url[127];
};
I need to call a function in this dll from my C# code, which has the following signature:
__declspec(dllexport) MyErrCode CheckUser(const struct CredData* cred_data, const struct EndpointData* endpoint_data);
Here is what I have tried:
I first declared the corresponding types in C# :
public struct CredPair
{
public string usr;
public string pas;
}
public enum CredType
{
PAIR,
KEY
}
public struct EndpointDirect
{
public string url;
}
public enum EndpointType
{
DIRECT
}
public struct CredData
{
public CredType credType;
public IntPtr credVal;
}
public struct EndpointData {
public EndpointType endpointType;
public IntPtr endpointVal;
}
Later declared the function as:
[DllImport("mydll.dll")]
public static extern MyErrCode CheckUser(CredData cred_Data, EndpointData endpoint_data);
and then call the function as:
CredData objCredData = new CredData();
objCredData.credType = CredType.PAIR;
CredPair objPair = new CredPair();
objPair.usr = "abc#xyz.com";
objPair.pas = "admin#1234";
IntPtr pnt = Marshal.AllocHGlobal(Marshal.SizeOf(objPair));
Marshal.StructureToPtr(objPair, pnt, false);
objCredData.credentialValue = pnt;
EndpointData objData = new EndpointData ();
objData.endpointType = EndpointType.DIRECT;
EndpointDirect epd = new EndpointDirect ();
epd.url = "example.com";
IntPtr urlptr = Marshal.AllocHGlobal(Marshal.SizeOf(epd));
Marshal.StructureToPtr(epd, urlptr, false);
objData.endpointValue = urlptr;
error_code = CheckUser(objCredData, objData);
But looks like parameters are not received correctly.
What parameters are wrong here? I believed void* in struct will become IntPtr in C#. The functions also need pointers to structure. Will that also have to be converted to IntPtr?
You have a number of issues with your PInvoke declarations
You need to make sure to free your HGlobal memory, otherwise it will leak.
The strings are declared as fixed size char arrays in C, so need to be CharSet.Ansi...
... and they need to be declared [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 127)] so they have fixed nested size.
The parameters to the function need to be ref, possibly with [In] attribute also.
You need CallingConvention = CallingConvention.Cdecl
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct CredPair
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 127)]
public string usr;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 127)]
public string pas;
}
public enum CredType
{
PAIR,
KEY
}
public enum EndpointType
{
DIRECT
}
public struct CredData
{
public CredType credType;
public IntPtr credVal;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct EndpointDirect
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 127)]
public string url;
}
public struct EndpointData
{
public EndpointType endpointType;
public IntPtr endpointVal;
}
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern MyErrCode CheckUser([In] ref CredData cred_Data, [In] ref EndpointData endpoint_data);
IntPtr pnt;
IntPtr urlptr;
try
{
CredPair objPair = new CredPair
{
usr = "abc#xyz.com"
pas = "admin#1234",
};
pnt = Marshal.AllocHGlobal(Marshal.SizeOf(objPair));
Marshal.StructureToPtr(objPair, pnt, false);
CredData objCredData = new CredData
{
credType = CredType.PAIR,
credVal = pnt,
};
endpointValue = new EndpointDirect
{
url = "example.com",
};
urlptr = Marshal.AllocHGlobal(Marshal.SizeOf(epd));
Marshal.StructureToPtr(epd, urlptr, false);
EndpointData objData = new EndpointData
{
endpointType = EndpointType.DIRECT,
endpointValue = urlptr,
};
error_code = CheckUser(ref objCredData, ref objData);
}
finally
{
Marshal.FreeHGlobal(pnt);
Marshal.FreeHGlobal(urlptr);
}
I could not find out what I'm doing wrong. I think I'm doing everything by the numbers, but it looks like I'm missing something.
On the managed side:
public override List<TimerPair> GetNativeTimers()
{
var listPtr = HgrDll.GetNativeTimers(out var size, out var ptrShift);
if (listPtr == IntPtr.Zero)
throw new NullReferenceException("sounds like the list"
+ " pointer was not given");
var timerPairList = new List<TimerPair>(size);
var structSize = Marshal.SizeOf(typeof(TimerPairStruct2));
for (var i = 0; i < size; i++)
{
var o = Marshal.PtrToStructure<TimerPairStruct2>(listPtr).ToTimerPair();
timerPairList.Add(o);
listPtr += structSize;
}
return timerPairList;
}
The target struct (used for intermediate compute) and class:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal struct TimerPairStruct2 : ITimerPairStruct
{
public long Time;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=64)]
public string Descr;
public TimerPair ToTimerPair() => new TimerPair(Descr, Time);
}
public class TimerPair
{
public string Descr { get; set; }
public TimeSpan Time { get; set; }
public TimerPair(string descr, long time)
{
Descr = descr;
Time = new TimeSpan(0,0,0,0, (int)((double)time/1000000));
}
}
For the Pinvoke part:
[DllImport("DwarfHomograph.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr GetNativeTimers(out int size, out int ptrShift);
and now on the native side:
DwarfHgr* DwarfAsset = nullptr;
extern "C" __declspec(dllexport) list<TimerPair>* GetNativeTimers(
int* size,
int* ptrShift)
{
return DwarfAsset->GetNativeTimers(size, ptrShift);
}
the used method from the DwarfHgr class:
list<TimerPair>* DwarfHgr::GetNativeTimers(int* size, int* ptrShift)
{
TimerPairsList = Sw.AsTimerPairs();
*size = static_cast<int>(TimerPairsList.size());
*ptrShift = sizeof(TimerPair);
return &TimerPairsList;
}
and the member is list<TimerPair> TimerPairsList;
The struct I'm marshaling is:
//#pragma pack(push,8)
struct __declspec(dllexport) TimerPair final
{
long long Time{}; // ns
char Descr[64]{};
};
//#pragma pack(pop)
I'm having unit tests in both native and managed sides; the expected result looks like (native):
ImageA processing: 22.0704ms
ImageB processing: 22.0448ms
MatFullA.copyTo: 1.87452ms
MatToSparse: 1.93482ms
calcOpticalFlowFarneback: 1115.48ms
create flowSparse image: 10.1416ms
But I have this (managed):
°óŠqÉ: 1,964,675.000000ms
q$Fý: 140,725,780.000000ms
xr$Fý: 140,725,780.000000ms
: 549,755.000000ms
à¡CUÉ: 1,964,675.000000ms
: 0.000000ms
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct sName_d
{
[MarshalAs(UnmanagedType.LPStr)]
public string szCountry;
[MarshalAs(UnmanagedType.LPStr)]
public string szCommonName;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct sCertData_d
{
public Int32 dwVersion;
public sName_d sIssuer;
public sName_d sSubject;
}
public void GenerateCert()
{
sCertData_d cert = new sCertData_d();
sName_d caIssuer = new sName_d();
caIssuer.szCountry = "US";
caIssuer.szCommonName = "John";
sName_d caSubject = new sName_d();
caSubject.szCountry = "UK";
caSubject.szCommonName = "Johann";
cert.sIssuer = caIssuer;
cert.sSubject= caSubject;
NativeMethods.GenerateCert(ref cert);
}
In the above code, NativeMethods.GenerateCert is an unmanaged function of C.
When Call reaches inside of this function, I am not getting the string values "John", "UK", "Johann" and "US".
[DllImport("AuthLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int GenerateCert(ref sCertData_d cert);
Unmanaged function prototype is like this -
typedef struct sName_d
{
char szCountry[0x35];
char szCommonName[0x35];
}sName_d;
typedef struct sCertData_d
{
int version;
sName_d sIssuer;
sName_d sSubject;
}sCertData_d;
int GenerateCert(const sCertData_d *psCert);
Your translation of sName_d is wrong. The unmanaged structure is:
typedef struct sName_d
{
char szCountry[0x35];
char szCommonName[0x35];
} sName_d;
These are inline character arrays. You marshaled these as UnmanagedType.LPStr. That's a pointer to null-terminated string. You need to use UnmanagedType.ByValTStr.
Used for in-line, fixed-length character arrays that appear within a structure. The character type used with ByValTStr is determined by the System.Runtime.InteropServices.CharSet argument of the System.Runtime.InteropServices.StructLayoutAttribute attribute applied to the containing structure. Always use the MarshalAsAttribute.SizeConst field to indicate the size of the array.
.NET Framework ByValTStr types behave like C-style, fixed-size strings inside a structure (for example, char s[5]).
Your translation should be:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct sName_d
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x35)]
public string szCountry;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x35)]
public string szCommonName;
}
Lets say I have the following structure in C
typedef struct
{
int field1;
char field2[16];
} MYSTRUCT;
Now I have a C routine that is called with a pointer to MYSTRUCT and I need to populate the structure, e.g.,
int MyCall(MYSTRUCT *ms)
{
char *hello = "hello world";
int hlen = strlen(hello);
ms->field1 = hlen;
strcpy_s(ms->field2,16,hello);
return(hlen);
}
How would I write MyCall in C#? I have tried this in Visual Studio 2010:
...
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit)]
public struct MYSTRUCT
{
[FieldOffset(0)]
UInt32 field1;
[FieldOffset(4)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
string field2;
}
public int MyProc(ref MYSTRUCT ms)
{
string hello = "hello world";
int hlen = hello.Length;
Marshal.Copy(hello, ms.field2, 0, hlen); // doesn't work
Array.Copy(hello, ms.field2, hlen); // doesn't work
// tried a number of other ways with no luck
// ms.field2 is not a resolved reference
return(hlen);
}
Thanks for any tips on the right way to do this.
Try changing the StructLayout.
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct MYSTRUCT
{
public UInt32 field1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string field2;
}
Since, you're passing as a reference, have you tried setting it as:
public int MyProc(ref MYSTRUCT ms)
{
string hello = "hello world";
ms.field2 = hello;
return hello.Length;
}
When using the ref keyword, you'll call MyProc like so:
static void Main(string[] args)
{
var s = new MYSTRUCT();
Console.WriteLine(MyProc(ref s)); // you must use "ref" when passing an argument
Console.WriteLine(s.field2);
Console.ReadKey();
}
As the topic says, trying to pass a struct from c# environnement to c++.
c++ code that defines both the struct and the interface:
#pragma pack(push, 4)
struct CEA708CONFIG
{
BYTE b608Service;
BYTE bCompactStream;
BYTE pActiveServices[63];
LONG lActiveServiceCount; //
POINT ptAlignmentPosition;
};
#pragma pack(pop)
interface
__declspec(uuid("{some clsid}"))
ICEA708Decoder : IUnknown {
virtual HRESULT SetConfig(IN const CEA708CONFIG* pConfig) = 0;
virtual HRESULT GetConfig(OUT CEA708CONFIG* pConfig) = 0;
};
now to the c# code, i defined the same struct in c#
[StructLayout(LayoutKind.Sequential, Pack = 4), Serializable]
public struct CEA708CONFIG
{
public byte is608Service;
public byte isCompactStream;
//[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)]
public IntPtr activeServices;
public long activeServiceCount;
public Point alignmentPosition;
};
and the corresponding interface that accepts the config structure
[ComVisible(true), ComImport, Guid("same clsid as above"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICEA708Decoder
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int SetConfig([In, MarshalAs(UnmanagedType.Struct)] ref CEA708CONFIG config);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetConfig([Out, MarshalAs(UnmanagedType.Struct)] out CEA708CONFIG config);
}
my problem occurs whenever i try to pass the structure, i can clearly see that while executing the c# code the entire struct is intialized with "reasonable" values, but once passed to the c++, i see that something has happened during the transaction.
the c# code that makes the magic happen:
CEA708CONFIG cc708Config;
ICEA708Decoder CC708DecoderConfig = CC708Filter as ICEA708Decoder;
if (CC708DecoderConfig == null)
{
throw new ApplicationException("Couldn't get ICEA708Decoder structure");
}
byte[] dataByte = new byte[63];
int size = Marshal.SizeOf(dataByte[0]) * dataByte.Length;
IntPtr pnt = Marshal.AllocHGlobal(size);
dataByte[0] = 1;
Marshal.Copy(dataByte, 0, pnt, dataByte.Length);
cc708Config.activeServices = pnt;
if (0 != (hr = CC708DecoderConfig.SetConfig(ref cc708Config)))
{
throw new ApplicationException("Couldn't SetConfig() because: " + DirectShowLib.DsError.GetErrorText(hr));
}
and the exception triggered by the SetConfig is:
{"Cannot marshal field 'activeServices' of type
'CCReIndexer.Graphs.CEA708CONFIG': Invalid managed/unmanaged type
combination (Int/UInt must be paired with SysInt or SysUInt).":""}
thanks for your help!!
Have you tried transfer array as array?
[StructLayout(LayoutKind.Sequential, Pack = 4), Serializable]
public struct CEA708CONFIG
{
public byte is608Service;
public byte isCompactStream;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)]
public byte[] activeServices;
public long activeServiceCount;
public Point alignmentPosition;
};
byte[] dataByte = new byte[63];
cc708Config.activeServices = dataByte;