2015-02-10 110 views
-2

我正在尝试使用以下C#代码创建指向非托管DLL函数的指针数组并将其传递给非托管DLL函数。将指针数组传递给非托管DLL函数

[DllImport("libantumbra.dll", CallingConvention = CallingConvention.Cdecl)] 
public static extern uint AnCtx_Init(IntPtr ctx); 

//create context 
this.ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr))); 
AnCtx_Init(ptr);//returns 0 (non-error) 
this.ctx = (IntPtr)Marshal.PtrToStructure(ptr, typeof(IntPtr)); 

[DllImport("libantumbra.dll", CallingConvention = CallingConvention.Cdecl)] 
public static extern int AnDevice_GetList(IntPtr ctx, out IntPtr outdevs, out int outndevs); 
IntPtr devs, ndevs; 
AnDevice_GetList(ctx, out devs, out ndevs); //exception occurs here 

但是,在我最后一次打电话时,我收到一个AccessViolationException。我认为它与我传递的数组指针有关,但是我一直无法找到解决方案。

我试图在这里实现的最终目标是传递一个指向AnDevice_GetList的指针,并且参数outdevs剩下一个已由DLL填充的数组。

让我知道你是否需要任何进一步的信息或有任何想法让我去尝试。

编辑:

这是我试图调用的函数。

头文件

An_DLL AnError AnDevice_GetList(AnCtx *ctx, AnDeviceInfo ***outdevs, 
          size_t *outndevs); 
typedef struct AnDevice AnDevice; 
typedef int AnError; 
typedef struct AnCtx AnCtx; 

与实现

AnError AnDevice_GetList(AnCtx *ctx, AnDeviceInfo ***outdevs, size_t *outndevs) 
{ 
    An_LOG(ctx, AnLog_DEBUG, "enumerate devices..."); 
    libusb_device **udevs; 
    ssize_t ndevs = libusb_get_device_list(ctx->uctx, &udevs); 
    if (ndevs < 0) { 
     An_LOG(ctx, AnLog_ERROR, "libusb_get_device_list: %s", 
       libusb_strerror(ndevs)); 
     return AnError_LIBUSB; 
    } 
    AnDeviceInfo **devs = malloc((ndevs + 1) * sizeof *devs); 
    if (!devs) { 
     An_LOG(ctx, AnLog_ERROR, "malloc: %s", strerror(errno)); 
     return AnError_MALLOCFAILED; 
    } 
    memset(devs, 0, (ndevs + 1) * sizeof *devs); 
    size_t j = 0; 
    for (ssize_t i = 0; i < ndevs; ++i) { 
     libusb_device *udev = udevs[i]; 
     AnDeviceInfo info; 
     An_LOG(ctx, AnLog_DEBUG, "device: bus %03d addr %03d", 
      libusb_get_bus_number(udev), libusb_get_device_address(udev)); 
     if (populate_info(ctx, &info, udev)) 
      continue; 
     An_LOG(ctx, AnLog_DEBUG, "vid 0x%04x pid 0x%04x", 
       info.devdes.idVendor, info.devdes.idProduct); 
     if (!match_vid_pid(info.devdes.idVendor, info.devdes.idProduct)) { 
      An_LOG(ctx, AnLog_DEBUG, " does not match Antumbra VID/PID"); 
      continue; 
     } 
     devs[j] = malloc(sizeof *devs[j]); 
     if (!devs[j]) { 
      An_LOG(ctx, AnLog_ERROR, "malloc: %s", strerror(errno)); 
      continue; 
     } 
     libusb_ref_device(udev); 
     *devs[j] = info; 
     ++j; 
    } 
    libusb_free_device_list(udevs, 1); 
    *outdevs = devs; 
    *outndevs = j; 
    return AnError_SUCCESS; 
} 
+0

未损坏的DLLS始终是最好的使用:-) – pm100 2015-02-10 17:44:06

+0

哎呀我的坏,修复。 – walshie4 2015-02-10 19:29:31

+0

这是完全错误的。然而,没有人知道什么是对的,因为我们看不到接口的另一端,或者知道如何调用它。所以,目前,没有详细信息,你是独立的。 – 2015-02-10 19:36:14

回答

0

有些东西没有意义在你的例子。您创建ptr2并为其分配空间,但不会将任何内容复制到该空间中,并且不会将其传递到AnDevice_GetList函数,因此看起来完全没有必要。您创建ptArray,但从未在任何地方使用它。

在这段代码中,你要创建的IntPtr结构的管理的阵列,并为他们每个人的分配内存为指向,和他们所指向的大小是一个指针的大小:

IntPtr[] ptArray = new IntPtr[] { 
    Marshal.AllocHGlobal(IntPtr.Size), 
    Marshal.AllocHGlobal(IntPtr.Size) 
}; 

要真正帮助我们需要清楚地了解AnDevice_GetList将要做什么。如果AnDevice_GetList正在填充指针数组,他们指向什么?他们是否指向由AnDevice_GetList分配的结构?如果是这样,那么你想要做的是创建一个IntPtr的数组,并在进行非托管调用时将其固定。由于您正在为要填充的调用创建一个数组,因此不要将该数组作为out参数传递。

[DllImport("libsomething.dll", CallingConvention = CallingConvention.Cdecl)] 
public static extern uint AnDevice_GetList(IntPtr outdevs); 

IntPtr[] ptArray = new IntPtr[numberOfPointersRequired]; 
GCHandle handle = GCHandle.Alloc(ptArray); 

try 
{ 
    AnDevice_GetList(handle.AddrOfPinnedObject()); 
} 
finally 
{ 
    handle.Free(); 
} 

我放弃了其他参数,因为我不知道你在做什么与他们或你如何期待他们被处理。

+0

谢谢你的回答,我会尝试你的方法,因为它是有道理的。我已经用getList的头文件和实现引用更新了我的文章。 – walshie4 2015-02-10 20:03:01

+0

我已更新我的帖子以包含您的建议,但我仍然收到异常。 – walshie4 2015-02-10 20:31:21

+0

@Erik你的签名完全不像被调用的非托管函数。 – 2015-02-10 20:33:06

0

你的非托管函数声明如下:

AnError AnDevice_GetList(AnCtx *ctx, AnDeviceInfo ***outdevs, size_t *outndevs) 

你应该把这一为:

[DllImport("libantumbra.dll", CallingConvention = CallingConvention.Cdecl)] 
public static extern int AnDevice_GetList(IntPtr ctx, out IntPtr outdevs, 
    out IntPtr outndevs); 

而这几乎是完全按照你做的事。唯一的区别是返回值为intoutndevs参数的类型为IntPtr。这是因为size_t在我意识到的平台上的指针大小。

这样称呼它:

IntPtr ctx = ...; // create a context somehow 
IntPtr devs; 
IntPtr ndevs; 
int retval = AnDevice_GetList(ctx, out devs, out ndevs); 
if (retval != AnError_SUCCESS) 
    // handle error 

所以,在这里您可以代码的问题呢?一个可能的解释是您传递的上下文无效。另一种可能是您执行64位代码,并且在您的翻译中不正确的尺寸outndevs导致错误。

这是使用p/invoke调用的非常难的API。你现在可以用devs来做什么。您可以轻松地将值复制到IntPtr[]阵列中。据推测,该库具有对这些不透明设备指针进行操作的功能。但是你必须保留devs并将其传回给库来释放它。推测图书馆出口功能做到这一点?


根据您的意见和各种更新,它看起来像你没有得到一个合适的上下文。我们只能猜测,但我希望AnCtx_Init被声明为

AnError AnCtx_Init(AnCtx **octx) 

这是一个指向不透明的背景下AnCtx*。翻译作为:

[DllImport("libantumbra.dll", CallingConvention = CallingConvention.Cdecl)] 
public static extern int AnCtx_Init(out IntPtr octx); 

这样称呼它:

IntPtr ctx; 
int retval = AnCtx_Init(out ctx); 
if (retval != AnError_SUCCESS) 
    // handle error 

,你现在要做的最重要的事情是开始检查错误。非托管代码不会抛出异常。你需要自己做错误检查。这很辛苦,但必须完成。一次完成一项功能。一旦你确定函数调用正在工作,继续下一步。

+0

是的,有一个freeList函数,以及其他如你所提到的那样作用于单个指针的函数。感谢您的回复,我现在将对其进行测试,并让您知道结果。 – walshie4 2015-02-10 20:34:49

+0

我已更新我的帖子,以反映我的新C#代码与您的建议,但异常继续发生。 – walshie4 2015-02-10 20:39:01

+0

最有可能的代码存在错误。我的答案中的p/invoke很好。我想你需要做一些调试。你没有显示任何迹象表明你的上下文是有效的。这让我怀疑你是否真的阅读了我的答案。随机尝试代码不会取得进步。你需要花点时间理解和调试和收集信息。 – 2015-02-10 20:39:24