如何从C#调用PInvoke C++ DLL函数

5
我正在尝试从我的C#应用程序中访问显微镜。该SDK是用C++编写的,由于它们是非托管代码,我无法将Dlls添加为我的应用程序的引用。
因此,我发现我需要使用DllImport来使用C#函数。
不幸的是,这似乎超出了我的能力范围。
以下是一些C++代码示例(从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);

并且这是稍后使用的方式:
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);

我该如何将这个转换为C#代码?我已经尝试过类似于以下的代码:
    [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")]

但基本上我不知道自己在做什么,也不知道下一步应该做什么(例如如何定义函数、如何处理C++代码中的“interface”、结构体是否转换正确等等)。


1
为什么要踩这个问题?虽然这不是我在这里看到的最好的问题,但我认为它并不值得被踩,这里所问的问题非常清晰明了... - ppetrov
相反,您可以创建一个托管VC++包装器,或考虑使用反射(虽然不建议...)请参阅托管和非托管代码互操作性的建议 - Scott Solmer
你还可以看看这个帖子:https://dev59.com/iXE85IYBdhLWcg3wvGER那里有一些有用的链接。 - ppetrov
1
这是一个非常不友好的API,结构体必须手动编组,并且没有足够的文档来猜测如何正确地执行操作,不清楚谁拥有该数组。最好的方法是联系相机供应商并寻求帮助,你几乎肯定不是第一个处理它的.NET程序员。 - Hans Passant
2个回答

3
首先,为了将那些数组转换成.NET的,你需要设置额外的属性。请参考链接
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct CameraInfo
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
    string camera_name;            // camera head type
...

接下来,方法CamDiscoverCameras返回一个指向结构体的指针,因此在.NET中,您将实际上会收到一个IntPtr, 然后使用Marshal.PtrToStructure方法将该指针转换为一个结构体:

[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;
}

不要忘记清理 - 从你的代码中看,结构体似乎是在C++代码中分配的,因此你可能需要将IntPtr传回C++代码以进行清理。


首先非常感谢您向我展示如何转换wchar_t和正确的函数声明。然而,当我使用您的代码时,我遇到了一个“System.BadImageFormatException”错误,其中包含“尝试加载具有不正确格式的程序。 (HRESULT异常:0x8007000B)”有什么想法吗? - Daniel M
@MarcMueller,我敢肯定问题出在你使用AnyCPU平台目标编译C#代码。重新设置项目(项目属性->生成->平台目标)为与Cam.dll相同的架构(x86 / x64),然后再试一次。 - Alovchin
没错,就是这样 - 你的代码可以工作了 :) 太棒了。 现在我会尝试使用我需要的其他函数 - 看看情况如何。 不过有一件事:你发表了“将IntPtr传递给C++代码以便它删除结构体,因为似乎是在那里分配的” - 我没有任何C++代码。上面的示例只是SDK中包含的一个示例应用程序,在我的应用程序中,我只使用dll,所以我不知道如何将任何东西传递给它。 - Daniel M
@MarcMueller 嗯,既然这个函数在堆中分配了结构体,那么必须有一种机制将其从内存中删除。查看库的API,也许有一些清理函数被导出。 - Alovchin

2

1
这最多只能算是一条注释。即使这是好建议,也应该作为注释。在这种情况下,PIA无法完成工作。 - David Heffernan
已经尝试过了。例如,对于wchar_t,它只是说“typedef”,没有进一步的解释 - 或者我只是太蠢无法使用该程序 - 如果是这种情况,那么它并不是很直观 :) - Daniel M
你说得对,我本来想发表评论的,但是我的声望不够,所以不能发表评论 :( - Michael Steinecke
1
这不是借口。恐怕您需要先获得声望,然后再发表评论。 - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接