我正在尝试使用以下C#代码创建并传递指向非托管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 :(得分: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();
}
我中断了其他参数,因为我不知道你对它们做了什么,或者你是如何期待它们被处理的。
答案 1 :(得分: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);
这几乎和你一样。唯一的区别是返回值为int
,outndevs
参数的类型为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
你现在要做的最重要的事情就是开始检查错误。非托管代码不会抛出异常。您需要自己进行错误检查。这很费劲,但必须这样做。一次一个功能。一旦确定函数调用正在运行,请转到下一个。