5

I am trying to access a Microscope from within my C# application. The SDK is written in C++ and I can not add the Dlls as references in my application (due to them being unmanaged code). As a result I found out that I will need to use DllImport in order to use the functions with C#.

Unfortunately this seems to be way over my head.

As an example some of the C++ code (from a sample app included in the SDK):

interface Camera;
typedef Camera * CameraHandle;

struct CameraInfo
{
  wchar_t               camera_name[64];        // camera head type
  wchar_t               controller_name[64];    // controller type
  wchar_t               firmware_version[64];   // firmware version
  long                  lib_id;                 // library ID
  long                  controller_id;          // controller ID
  long                  camera_id;              // camera ID
  CameraHandle          handle;                 // handle to opened camera
  long                  status;                 // status (0 = available)
  CameraInfo()
  {
    memset(this,0,sizeof(*this));
  }
};
typedef struct CameraInfo CameraInfo;

CamDiscoverCameras(OUT const struct CameraInfo ** info, OUT long * count);

And this is how it is used later on:

CamResult               result;          //enum CamResult{...}
const CameraInfo*       camera_info  = NULL;
long                    camera_count = 0, pre_lib_id, pre_controller_id;

result = CamDiscoverCameras(&camera_info, &camera_count);

How do I convert this to C# code? I have already tried something like:

    [StructLayout(LayoutKind.Sequential)]
    struct CameraInfo
    {
        string              camera_name;            // camera head type
        string              controller_name;        // controller type
        string              firmware_version;       // firmware version
        int                 lib_id;                 // library ID
        int                 controller_id;          // controller ID
        int                 camera_id;              // camera ID
        public IntPtr       handle;                 // handle to opened camera
        int                 status;                 // status (0 = available)
    }

    [DllImport("Cam.dll", EntryPoint = "CamDiscoverCameras")]

But basically I have no idea of what I am doing and what needs to be done next (like how the function needs to be defined, how to deal with that "interface" in the C++ code, is the structure converted correctly and so on).

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Daniel M
  • 105
  • 2
  • 11
  • 1
    Why the downvote? It's not the best question I have seen here but I don't think it deserves a downvote, what is asked here is quite clear... – ppetrov Oct 09 '14 at 11:22
  • Instead you could create a [managed](http://www.c-sharpcorner.com/UploadFile/SamTomato/managed-cpp-wrapper-for-unmanaged-code/) VC++ [wrapper](http://msdn.microsoft.com/en-us/library/c0sfktfw%28v=vs.110%29.aspx), or consider using reflection (although that is not recommended...) Take a look at [Recommendations for Managed and Unmanaged Code Interoperability](http://msdn.microsoft.com/en-us/library/ms993883.aspx). – Scott Solmer Oct 09 '14 at 11:25
  • You can also takke a look at this post: http://stackoverflow.com/questions/2637571/creating-simple-c-net-wrapper-step-by-step there are some links that can be usefull – ppetrov Oct 09 '14 at 11:26
  • 1
    It is a very unfriendly api, the structs have to marshaled by hand and there is not enough documentation to guess how to do it correctly, unclear who owns the array. Best way to go about it is to contact the camera vendor and ask for help, you are almost surely not the first .NET programmer to deal with it. – Hans Passant Oct 09 '14 at 11:45

2 Answers2

3

First of all, to convert those wchar_t arrays to .NET string you will need to set additional attributes.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct CameraInfo
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
    string camera_name;            // camera head type
...

Next, the method CamDiscoverCameras returns a pointer to the struct, so in .NET you will be actually receiving an IntPtr and then converting that pointer to a struct using Marshal.PtrToStructure method:

[DllImport("Cam.dll", EntryPoint = "CamDiscoverCameras")]
// you can create enum of ints here and return it
static extern int CamDiscoverCameras(out IntPtr info, out int count);

CameraInfo DiscoverCameraInfos()
{
    IntPtr info; int count;
    int camResult = CamDiscoverCameras(out info, out count);
    var camInfo = (CameraInfo) Marshal.PtrToStructure(info, typeof(CameraInfo));
    // cleanup code - pass your IntPtr to C++ code
    // for it to delete the struct as it seems it was allocated there
    return camInfo;
}

And don't forget to cleanup - from your code it seems that the structure is being allocated somewhere in C++ code, so you likely would have to pass the IntPtr back to C++ code for it to deallocate it.

Alovchin
  • 663
  • 3
  • 9
  • First of all thank you very much for showing me how to convert the wchar_t and the correct function declaration. However when I use your code I get a "System.BadImageFormatException" with "An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)" Any idea? – Daniel M Oct 09 '14 at 13:15
  • @MarcMueller I'm pretty sure it's because you are compiling your C# code with AnyCPU platform target. Retarget your project (project properties -> Build -> Platform target) to the same architecture that your Cam.dll was built for (x86 / x64) and try again. – Alovchin Oct 09 '14 at 13:17
  • yep that was it - your code works :) awesome. Now I will try to do this with the rest of the functions I need - will see how that goes. One thing though: you posted "pass your IntPtr to C++ code for it to delete the struct as it seems it was allocated there" - I don't have any C++ code. The example above was just from a sample app included in the SDK, in my application I only use the dll so I have no idea how to pass anything to it. – Daniel M Oct 09 '14 at 13:45
  • @MarcMueller Well, since this function allocates the structure in the heap, there must be a mechanism to remove it from memory. Look at the API of the library, maybe there are some cleanup functions exported. – Alovchin Oct 09 '14 at 13:47
2

Your struct definition in C# is a bit more complex then yours. Have a look at the PInvoke Interop Assistant. https://clrinterop.codeplex.com/releases/view/14120 It helps a lot with the initial attributes.

  • 1
    This should be a comment at best. Even if this were good advice, it should be a comment. In this case PIA isn't going to get the job done. – David Heffernan Oct 09 '14 at 12:35
  • Already tried that. For example for wchar_t it only says "typedef" and no further explanation - or I am just too dumb to use the program - in which case it is not very intuitive :) – Daniel M Oct 09 '14 at 13:17
  • You're right, I intended to make a comment, however I'm not allowed to. Not enough reputation :( – Michael Steinecke Oct 09 '14 at 15:33
  • 1
    That's no excuse. I'm afraid you need to get the rep first and then comment. – David Heffernan Oct 09 '14 at 19:57