C# 将指针数组传递给非托管DLL函数
我试图使用下面的C#代码创建并传递指向非托管DLL函数的指针数组 然而,在我最后一次通话时,我收到了一个C# 将指针数组传递给非托管DLL函数,c#,dll,pinvoke,dllimport,C#,Dll,Pinvoke,Dllimport,我试图使用下面的C#代码创建并传递指向非托管DLL函数的指针数组 然而,在我最后一次通话时,我收到了一个AccessViolationException。我认为这与我传递的数组指针有关,但是我还没有找到解决方案 我在这里试图实现的最终目标是将指针传递给AndDevice_GetList,并使用参数outdevs留下一个由DLL填充的数组 让我知道如果你需要任何进一步的信息或有任何想法让我尝试 编辑: 这是我试图调用的函数 头文件: An_DLL AnError AnDevice_GetList(
AccessViolationException
。我认为这与我传递的数组指针有关,但是我还没有找到解决方案
我在这里试图实现的最终目标是将指针传递给AndDevice_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;
}
a错误和设备列表(AnCtx*ctx、AnDeviceInfo***outdevs、size\u t*outndevs)
{
日志(ctx、AnLog调试,“枚举设备…”);
libusb_设备**udevs;
ssize_t ndevs=libusb_get_device_list(ctx->uctx,&udevs);
如果(无损检测值<0){
日志(ctx,日志错误,“libusb获取设备列表:%s”,
利布斯特罗(ndevs);
返回一个错误的LIBUSB;
}
AnDeviceInfo**devs=malloc((ndevs+1)*大小*devs);
如果(!devs){
日志(ctx,AnLog错误,“malloc:%s”,strerror(errno));
返回一个错误\u MALLOCFAILED;
}
成员集(开发者,0,(NDEV+1)*大小*DEV);
尺寸j=0;
对于(ssize_t i=0;i
在您的示例中,有些事情没有意义。您可以创建ptr2
并为其分配空间,但从不将任何内容复制到该空间中,也不会将其传递给AndDevice\u GetList函数,因此这似乎完全没有必要。您创建了ptArray
,但也从不在任何地方使用它
在这段代码中,您正在创建一个IntPtr结构的托管数组,并为每个结构分配内存,它们所指向的大小是单个指针的大小:
IntPtr[] ptArray = new IntPtr[] {
Marshal.AllocHGlobal(IntPtr.Size),
Marshal.AllocHGlobal(IntPtr.Size)
};
为了真正提供帮助,我们需要清楚地了解和vice\u GetList
将要做什么。如果andvice\u GetList
正在填充指针数组,它们指向什么?它们是否指向由和device\u 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();
}
我省略了其他参数,因为我不知道您在用它们做什么,也不知道您希望如何处理它们。您的非托管函数声明如下:
AnError AnDevice_GetList(AnCtx *ctx, AnDeviceInfo ***outdevs, size_t *outndevs)
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
IntPtr ctx;
int retval = AnCtx_Init(out ctx);
if (retval != AnError_SUCCESS)
// handle error
你应该把它翻译成:
[DllImport("libantumbra.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AnDevice_GetList(IntPtr ctx, out IntPtr outdevs,
out IntPtr outndevs);
[DllImport("libantumbra.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AnCtx_Init(out IntPtr octx);
这几乎和你做的一样。唯一的区别是返回值是int
,而outndevs
参数的类型是IntPtr
。这是因为size\u t
在我所知道的平台上是指针大小的
可以这样称呼:
AnError AnDevice_GetList(AnCtx *ctx, AnDeviceInfo ***outdevs, size_t *outndevs)
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
IntPtr ctx;
int retval = AnCtx_Init(out ctx);
if (retval != AnError_SUCCESS)
// handle error
那么,你的代码哪里会出错呢?一种可能的解释是,您传递的上下文无效。另一种可能是您执行64位代码,翻译中的outndevs
大小不正确导致了错误
这是一个很难使用p/invoke调用的API。使用devs
现在可以做什么。您可以很容易地将值复制到IntPtr[]
数组中。想必库中有一些函数可以在这些不透明的设备指针上运行。但是您必须保留devs
,并将其传递回库以取消分配。想必库导出了一个函数来实现这一点
根据您的评论和各种更新,您似乎没有获得正确的上下文。我们只能猜测,但我希望
AnCtx\u Init
被声明为
AnError AnCtx_Init(AnCtx **octx)
这是指向不透明上下文的指针AnCtx*
。将其翻译为:
[DllImport("libantumbra.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AnDevice_GetList(IntPtr ctx, out IntPtr outdevs,
out IntPtr outndevs);
[DllImport("libantumbra.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AnCtx_Init(out IntPtr octx);
可以这样称呼:
AnError AnDevice_GetList(AnCtx *ctx, AnDeviceInfo ***outdevs, size_t *outndevs)
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
IntPtr ctx;
int retval = AnCtx_Init(out ctx);
if (retval != AnError_SUCCESS)
// handle error
现在你必须做的一件大事是开始检查错误。非托管代码不会引发异常。您需要自己进行错误检查。这是一项艰巨的任务,但必须完成。一次完成一个功能。一旦确定某个函数调用正在工作,请转到下一个。未损坏的DLL始终是最好的使用方法:-)哎呀,我的糟糕,修复了。这是完全错误的。然而,没有人知道什么是正确的,因为我们看不到接口的另一面,也不知道如何调用它。因此,目前没有任何细节,你只能靠自己。首先,问问自己,除了它们被泄露之外,
ptArray
和ptr2
会发生什么。任何地方都没有传递数组。可能你只是对非托管函数的翻译有误,不管它是什么。顺便说一句,adam nathans book.NET和COM是这类问题的圣经谢谢你的回答,我会尝试你的方法,因为它有意义。我已经用getList的标题和实现参考更新了我的帖子。我已经更新了我的帖子,包括了你的建议,但是我仍然收到了例外