c# struct defined as:
[StructLayout(LayoutKind.Sequential)]
public struct RecognizeResult
{
/// float
public float similarity;
/// char*
[MarshalAs(UnmanagedType.LPStr)]
public string fileName;
}
c function signature:
void FaceRecognition(RecognizeResult *similarity); //where similarity is a pointer to an array
P/Invoke signature:
[DllImport(DllName, EntryPoint = "FaceRecognition")]
public static extern void Recognize(ref RecognizeResult similarity);
this is how i call the c++ function in managed code:
RecognizeResult[] results = new RecognizeResult[100];
Recognize(ref results[0]); //through p/invoke
it turns out the array can't be passed to unmanaged code, only the first element is passed.
how should i do to pass an array to unmanaged code (is it even possible)?
BTW, Do i have to pin the array when calling unmanaged code so that GC won't move the array?
Try this:
[DllImport(DllName, EntryPoint = "FaceRecognition")]
public static extern void Recognize(RecognizeResult[] similarity);
RecognizeResult[] results = new RecognizeResult[100];
// fill array elements
Recognize(results);
Related
I am working in C# and I need to call a function in a C++ dll library. This function returns a struct but I can´t get anything.
This is the function I need to call and the struct that returns in C++ library:
ATHENA_API _DEVICE_INFO* __stdcall GetDeviceInfoKeepConnection(_DEVICE_INFO* pDeviceInfo);
typedef struct TD_DEVICE_INFO{
TCHAR chDeviceName[256];
int nCommPort;
int nECGPos;
int nNumberOfChannel;
int nESUType;
int nTymestampType;
int nDeviceHandle;
TCHAR chDeviceID[260];
}_DEVICE_INFO;
This is my C# code trying to call the function:
[DllImport(#"\BAlertSDK\ABM_Athena.dll")]
static extern _DEVICE_INFO GetDeviceInfoKeepConnection(_DEVICE_INFO deviceInfo);
[StructLayout(LayoutKind.Sequential)]
struct _DEVICE_INFO
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string chDeviceName;
public int nCommPort;
public int nECGPos;
public int nNumberOfChannel;
public int nESUType;
public int nTymestampType;
public int nDeviceHandle;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string chDeviceID;
}
void Test_Click()
{
_DEVICE_INFO d = new _DEVICE_INFO();
_DEVICE_INFO deviceInfo = GetDeviceInfoKeepConnection(d);
}
The only I can get is an empty _DEVICE_INFO object. I think my problem is that I am not defining correctly the DEVICE INFO struct.
I have never worked with dll´s to this level. Can you help me?
Thanks in advance.
Thanks to all!! The problem has solved with this:
Parameter pass by reference and struct charset Unicode.
It seems that the function GetDeviceInfoKeepConnection returns a pointer to _DEVICE_INFO struct. So, in your C# code, you need to change the definition of the function to:
[DllImport(#"\BAlertSDK\ABM_Athena.dll")]
static extern IntPtr GetDeviceInfoKeepConnection(IntPtr deviceInfo);
And then you can access the struct data like this:
void Test_Click()
{
_DEVICE_INFO d = new _DEVICE_INFO();
IntPtr pDeviceInfo = Marshal.AllocHGlobal(Marshal.SizeOf(d));
Marshal.StructureToPtr(d, pDeviceInfo, false);
IntPtr deviceInfo = GetDeviceInfoKeepConnection(pDeviceInfo);
_DEVICE_INFO result = (_DEVICE_INFO)Marshal.PtrToStructure(deviceInfo, typeof(_DEVICE_INFO));
Marshal.FreeHGlobal(pDeviceInfo);
}
Note that, to ensure that the memory is cleaned up after use, you should use the Marshal.FreeHGlobal method to free the memory that was allocated by Marshal.AllocHGlobal.
let say we have following code in a dll written in C-lang, where i try to map some functions defined in the dll as functionpointers, map to their actual functions, i follow following link to get till here
https://learn.microsoft.com/en-us/dotnet/framework/interop/marshaling-different-types-of-arrays
Dlltest.h
typedef struct VersionInfo
{
UINT uMajor;
UINT uMinor;
UINT uMRevision;
} STRUCT_VERSION_INFO;
typedef struct DllTestFPStruct
{
int(*Close) (void);
int(*Init) (STRUCT_VERSION_INFO *sVersInfo);
int(*RegisterClient) (int id);
} STRUCT_DLL_TEST_FP_STRUCT;
typedef struct DllTestMgr
{
STRUCT_DLL_TEST_FP_STRUCT *psItf;
int iDummy;
} STRUCT_DLL_TEST_MGR;
extern "C"
{ __declspec(dllexport) void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP); }
Dlltest.c
static int Close(void);
static int Init(STRUCT_VERSION_INFO *sVersInfo);
static int RegisterClient(int id);
STRUCT_DLL_TEST_FP_STRUCT sFP =
{
&Close,
&Init,
&RegisterClient,
};
DLLTESTC_API void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP)
{ psFP->psItf = &sFP; }
static int Close(void)
{ printf("Closed called.\n"); }
static int Init(STRUCT_VERSION_INFO *sVersInfo)
{ printf("Init called.\n");}
static int RegisterClient(STRUCT_VERSION_INFO *sVersInfo)
{ printf("RegisterClient called.\n");}
Now i want to write a c# application which uses this DLL, specially it should make use of the "GetDllTestFP"-Function which maps the functionpointers to their actuall function. right now my C#-Application is as follow:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int FP_DLL_TEST_CLOSE(ref VersionInfo sVersInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int FP_DLL_TEST_INIT(ref VersionInfo sVersInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int FP_DLL_TEST_RC(ref VersionInfo sVersInfo);
[StructLayout(LayoutKind.Sequential)]
public struct DllTestFPStruct
{
public FP_DLL_TEST_CLOSE Close;
public FP_DLL_TEST_INIT Init;
public FP_DLL_TEST_RC RegisterClient;
}
[StructLayout(LayoutKind.Sequential)]
public struct DllTestMgr
{
public IntPtr psItf;
public int iDummy;
}
[DllImport("DllTestC.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void GetDllTestFP(ref DllTestMgr ps);
static void Main(string[] args)
{
VersionInfo vInfo = new VersionInfo();
DllTestFPStruct dllFPs = new DllTestFPStruct();
DllTestMgr dllTest = new DllTestMgr();
IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(dllFPs));
Marshal.StructureToPtr(dllFPs, buffer, false);
dllTest.psItf = buffer;
GetDllTestFP(ref dllTest); // Funtionpointers are not mapped, still null
dllFPs.Close(ref vInfo);
}
The problem is, that the functions do not get mapped to their actuall functions in the dll.
Any idea how can i achieve my goal?
Thanks
The C# code is:
DllTestMgr dllTest = new DllTestMgr();
GetDllTestFP(ref dllTest);
DllTestFPStruct dllFPs = (DllTestFPStruct)Marshal.PtrToStructure(dllTest.psItf, typeof(DllTestFPStruct));
VersionInfo vInfo = new VersionInfo();
dllFPs.Close(ref vInfo);
You don't need to allocate dllTest.psItf becase in GetDllTestFP you do:
DLLTESTC_API void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP)
{
psFP->psItf = &sFP;
}
So you copy the address of sFP.
Note that in general this is a bad idea, because you are giving to the "client" direct access to your data (the sFP struct). The alternative is that the client passes the memory (as you wrote before) and then you do:
(*psFP->psItf) = sFP;
(but then remember to free the allocated memory!)
Third alternative, the C-side allocates a block of memory through a shared allocator (one that can be used by C#, so no malloc/new here) and then the C# has to deallocate it.
wrong solution is
STRUCT_DLL_TEST_FP_STRUCT sFP2 = sFP;
psFP->psItf = &sFP2;
The lifetime of sFP2 ends when the method returns. psFP->psItf is now pointing to a piece of stack that doesn't exist anymore. don't do it!
Ah... as written by #Hans Passant, depending on who allocates the memory, the GetDllTestFP can be ref or out. If the memory is allocated by C# then it must be ref, if it isn't allocated (as is now) or is allocated by C++, then out is ok and you'll save on the marshaling in one direction.
I have a C structure used for callback that I need to marshall to C# .NET:
struct CTMDeviceInfo {
enum CTMDeviceType eDeviceType;
char * szDeviceModel;
char * szDeviceSubModel;
int32_t * piDeviceID;
};
This is my C# version:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CTMDeviceInfo
{
public CTMDeviceType deviceType;
[MarshalAs(UnmanagedType.LPStr)]
public string deviceModel;
[MarshalAs(UnmanagedType.LPStr)]
public string deviceSubModel;
public IntPtr deviceId;
};
Which is used inside another structure:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CTMDeviceError
{
public CTMDeviceInfo deviceInfo;
[MarshalAs(UnmanagedType.I4)]
public Int32 resultCode;
[MarshalAs(UnmanagedType.I4)]
public Int32 extendedResultCode;
public IntPtr denomination;
public IntPtr changeDue;
};
My problem is that the "IntPtr deviceId" does not consistently return the correct value every time a callback was made.
I was expecting an integer value of 5, 15 or 16 but it keeps returning random values like 106, 865412, 652272, etc.
I don't know what I did wrong. What I did though is to prevent the callback in my managed code to be garbage collected using GCHandle.
Here is the sequence on how I did it:
From my unmanaged code I have this CDECL callback method:
void ctm_add_device_error_event_handler(CTMDeviceErrorCallback);
typedef void (CTMDeviceErrorCallback) (struct CTMEventInfo, struct CTMDeviceError );
This is my managed code:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void OnDeviceErrorCallBack(CTMEventInfo evtInfo, CTMDeviceError deviceError);
[DllImport("libctmclient-0.dll", EntryPoint = "ctm_add_device_error_event_handler", CallingConvention = CallingConvention.Cdecl)]
public static extern void AddDeviceErrorEventHandler([MarshalAs(UnmanagedType.FunctionPtr)] OnDeviceErrorCallBack deviceErrorCallBack);
OnDeviceErrorCallBack deviceErrorCallback;
GCHandle deviceErrorCallbackGCHandle;
deviceErrorCallback = new OnDeviceErrorCallBack(OnDeviceError);
deviceErrorCallbackGCHandle = GCHandle.Alloc(deviceErrorCallback);
AddDeviceErrorEventHandler(deviceErrorCallback);
And this is where the callback is handled:
public void OnDeviceError(CTMEventInfo evtInfo, CTMDeviceError deviceError)
{
int nDeviceId = Marshal.ReadInt32(deviceError.deviceInfo.deviceId);
}
I tried to use unsafe to use pointers directly but the issue is still the same.
public unsafe int *deviceId; //instead of IntPtr
int nDeviceId = 0;
unsafe
{
nDeviceId = *(deviceError.deviceInfo.deviceId);
}
I'm sure that my unmanaged code returned the correct value because I have logs but when it reached in my managed code, somehow another value was returned.
It's like it is reading on a different reference or something.
Hope somewhat could help me because I am stuck for a while now.
Thanks!
Use following c# structure. You can get the two string from the pointer later in the code. :
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CTMDeviceInfo
{
public CTMDeviceType deviceType;
public IntPtr deviceModel;
public IntPtr deviceSubModel;
public IntPtr deviceId;
};
I am using pinvoke in a project I am building.
I need to get data from a function in C, the function gets pointer to a struct.
In my c# I did a class with the appropriate attribute(layountkind.sequntial).
Before the function I do:
mystruct str=new mystruct();
str.Data=new byte[14];
func(str);
I fill the struct in the function but when it exit the function the instance of the class doesn't have the values i filled in c,i check the content of the pointer before i exit the c function and it has the right values.
Below is the prototype of the function:
[DllImport("mydll.dll", CallingConvention=CallingConvention.Cdecl)]
void func([MarshalAs(UnmanagedType.LPStruct)] mystruct str);
My struct in C#:
[StructLayout(LayoutKind.Sequential)]
public class mystruct
{
public ushort familiy;
[MatshalAs(UnmanagedType.ByValArray,SizeConst=14)]
public byte [] data;
}
function and struct in C:
struct sockaddr{
unsigned short familiy;
char data [14];
};
void func(struct sockaddr *info)
{
int i;
char buffer[100]
recvfrom(sockfd,buffer,0,info,&i);//assume i have a global varible sockfd and it is an open socket
}
How can i fix my problem?
The p/invoke declaration is incorrect. The default marshalling is In only. But you need Out marshalling. Otherwise the marshaller won't attempt to marshal the data set by the unmanaged function back to the managed class.
So you can fix the problem by declaring the function like this:
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void func([Out] mystruct str);
Personally I think that it would be more idiomatic to use a C# struct. I'd declare it like this:
[StructLayout(LayoutKind.Sequential)]
public struct mystruct
{
public ushort family;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=14)]
public byte[] data;
}
And then the function becomes:
[DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
static extern void func(out mystruct str);
And the call is simply:
mystruct str;
func(out str);
I'm stuck by passing struct with string data from C# code to C++ dll.
c++ code
typedef struct
{
LPCSTR lpLibFileName;
LPCSTR lpProcName;
LPVOID pPointer1;
LPVOID pPointer2;
} ENTITY, *PENTITY, *LPENTITY;
extern "C" __declspec(dllexport) int Test(LPENTITY entryList, int size);
int Test(LPENTITY entryList, int size)
{
for (int i = 0; i < size; i++)
{
ENTITY e = entryList[i];
// the char* value doesn't get passed correctly.
cout << e.lpLibFileName << e.lpProcName << endl;
}
return 0;
}
c# code
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private class Entity
{
public string lpLibFileName;
public string lpProcName;
public IntPtr pPointer1;
public IntPtr pPointer2;
}
[DllImport("cpp.dll")]
private static extern int Test(
[In, Out, MarshalAs(UnmanagedType.LPArray)]Entity[] entities,
int size);
static void Main(string[] args)
{
var entries = new[]
{
new Entity
{
lpLibFileName = "comdlg32",
lpProcName = "PrintDlgExW",
pPointer1 = Marshal.GetFunctionPointerForDelegate(new PrintDlgEx(PrintDlgExCallback)),
pPointer2 = IntPtr.Zero,
},
new Entity
{
lpLibFileName = "shell32",
lpProcName = "ShellAboutW",
pPointer1 = Marshal.GetFunctionPointerForDelegate(new ShellAbout(ShellAboutCallback)),
pPointer2 = IntPtr.Zero,
},
};
var ret = Test(entries, entries.Length);
}
The PINVOKE was triggered, but the char* data like lpLibFileName and lpProcName cannot be passed correctly. Did I miss something? How to correct it?
Thanks.
Your code maps a C# class onto a native struct. Because a C# class is a reference type then it will be marshalled as a reference. So your code passes an array of references that gets marshalled to an array of pointers on the native side.
But the native code expects a pointer to an array of structs which are value types. So the simplest solution is to change the declaration of Entity to be a struct rather than a class.
The other issues that I can see:
The native code appears to be using the cdecl calling convention. You'll need to change the C# code to match.
You are decorating the array parameter with Out. You cannot marshal modifications to the string fields back to the managed code.
You will need to make sure that you keep alive the delegates that you pass to GetFunctionPointerForDelegate to stop them being collected.
When passing parameter like array of custom structures, use 'struct' instead of 'class' when defining your own data structure. After I changing it back to struct, everything worked fine.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private struct Entity
{
public string lpLibFileName;
public string lpProcName;
public IntPtr pPointer1;
public IntPtr pPointer2;
}