Reputation: 113
I'm currently trying to write a wrapper for a USB camera that uses an unmanaged C DLL to interface. The supplied software is written in C++ using Qt, and I'm trying to integrate this camera in a C# application I've already written (the application already uses a different camera that is supplied with a C# interface). I've read a bunch about unmanaged/managed interfacing, but I still am getting stuck with this specific DLL.
I'm having trouble dealing with P/Invoking the DLL. I've done a bunch of reading on ways to deal with managed/unmanaged code, and it seems that since I only have the functions/parameters (and nothing else), I can't write a C++/CLI wrapper and am forced to stick to P/Invoke. If this is untrue, some help with an alternate method would be appreciated, if more efficient.
Anyhow, I'm currently having trouble calling some (read: all) of the functions. For examples sake, let's pick this function (taken from dijSDK.h, the header file to be used when using this interface in unmanaged C++ in Qt):
/** \brief Find cameras currently connected to the host
*
* \param[out] pGuidList List of unique identifiers of connected cameras; memory is allocated by user
* \param[in,out] pNumGuids Pointer to the number of elements in pGuidList to limit the search
* \n Pointer to the number of cameras found
* \param[in] mask optional mask to limit the results in pGuidList
*
* \note
* - the function lists all supported cameras that are connected to the host having a driver installed
* - the cameras are identified by a string in a defined style:
* \n <i>[Name of the camera library resp. camera class]:[camera name]:[Serial number of the camera]</i>
* - the optional parameter mask may be used to limit the results returned in pGuidList;
* \n mask is in the same style as the results in pGuidList
* therefore the results can be limited to certain camera classes, camera types,
* and cameras with a given serial number */
DIJSDK_EXPORT error_t DijSDK_FindCameras(DijSDK_CamGuid* pGuidList, unsigned int* pNumGuids, const DijSDK_CamGuid mask = NULL);
DIJSDK_EXPORT is a define as follows:
#ifndef DIJSDK_EXPORT
#define DIJSDK_EXPORT externC DLLEXPORT
#endif
And the two typedefs used in the above function:
/// Return value of all DijSDK functions
/// \note All return values of type error_t can be casted to the enum <b>DijSDK_EErrorCodeList</b>, see header errorlistinstall.h
typedef int error_t;
// unique DijSDK types
/// Globally unique identifier for all supported cameras. It is used to establish a relation between <b>physical</b> and <b>logical</b> cameras.
typedef char DijSDK_CamGuid[64];
So looking at the function, the user passes in an allocated string array and the number of allocated strings in that array, and the function should return a bunch of strings and the number of strings it returns. I have absolutely no idea how to implement this in C#. I've tried different DllImport
declarations of the function, like passing pGuidList as a...
StringBuilder
Where the variable I passed in was initialized with var = new StringBuilder(64)
and the uint
passed in was just 1.
StringBuilder[]
Where the variable I passed in was initialized with var = new StringBuilder[length]
, where length
was the uint
passed to the function.
IntPtr
Where the variable I passed in was initialized with Marshal.AllocCoTaskMem(64 * length * Marshal.SystemDefaultCharSize)
, where length
is the same uint as aboveAnd then passed in pNumGuids
as ref uint
. I don't hand in an argument for mask
since it is optional, and I don't need to use it.
I constantly get a PInvokeStackImbalance
since the signature of the managed import and the unmanaged DLL don't match, but I can't figure out what the correct signature is.
I've got questions about all of the functions I'm dealing with, but I'll be dealing with them one at a time. The first function I use is an "init" function that takes in a function pointer argument and a void pointer argument for a callback and it's associated data, but I can call it with no arguments if I don't need to use the callback and that is P/Invoking just fine. The function in this question is the one I need to call after to get a list of attached devices. I think if I can get this one working, I'll be able to figure out most of the rest of the functions.
The SDK and documentation I'm working with isn't publicly available online from the company that supplies it. If anyone would like it to see all of the code/documentation, I can post it on a fileserver and post a link to it.
Any help would be much appreciated! If there's anything that I left out, make sure to heckle me about it =P
Solution: After the help, I managed to get it working:
The DllImport:
[DllImport(DLL,CallingConvention=CallingConvention.Cdecl)]
public static extern error_t DijSDK_FindCameras([Out] DijSDK_CamGuid[] pCamGuid, ref uint pNumGuids, string mask);
The struct to use for DijSDK_CamGuid:
[StructLayout(LayoutKind.Sequential,Size=64)]
public struct DijSDK_CamGuid
{
[MarshalAs(UnmanagedType.ByValArray,SizeConst=64)]
public char[] id;
}
And the findCameras()
function:
private void findCameras()
{
uint cameralistlen = 5;
DijSDK_CamGuid[] cameralist = new DijSDK_CamGuid[cameralistlen];
result = Jenoptik.DijSDK_FindCameras(cameralist, ref cameralistlen, null);
}
Upvotes: 1
Views: 1722
Reputation: 321
Try declaring this way:
[DllImport("whatever.dll", CallingConvention = CallingConvention.Cdecl)]
extern public static int DijSDK_FindCameras(byte[] pGuidList, ref int pNumGuids, byte[] mask);
and using it like this:
byte[] CamGuids = new byte[64 * 32]; // Up to 32 cameras
int GuidsCount = CamGuids.Length / 64;
int result = DijSDK_FindCameras(CamGuids, ref GuidsCount, null);
Upvotes: 1
Reputation: 10591
Do you need to deal with the id in managed code as a string? If not, perhaps something like this would work, treating the bytes as an opaque byte array:
[StructLayout(LayoutKind.Sequential)]
public struct DijSDK_CamGuid
{
[MarshalAs(UnmanagedType.ByValAray,SizeConst=64)]
public byte[] id;
}
Then declare your p/invoke signature to use take a parameter of type:
DijSDK_CamGuid*
Upvotes: 1