使用 PInvoke char** (C -> C#) 更正 return 值
Correct return value with PInvoke char** (C -> C#)
我的问题是我正在使用 C# 中的 MATLAB API,这是给我带来麻烦的函数。
C代码:
EXTERN_C char ** matGetDir(MATFile * pMF, int *num);
我希望它能工作,但不幸的是它没有(C# 代码):
[DllImport("libmat.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern string[] matGetDir(IntPtr matFile, ref int num);
如果我用 IntPtr
而不是 string[]
我可以调用函数,但是我不知道如何将代码从 IntPtr
转换为 string[]
编辑 1:
我也尝试使用这些属性,但它失败并出现错误:无法封送 'return value':无效的 managed/unmanaged 类型组合。
[return: MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)]
private static extern string[] matGetDir(IntPtr matFile, ref int num);
你可以这样做:
[DllImport("libmx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void mxFree(IntPtr ptr);
[DllImport("libmat.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern IntPtr matGetDir(IntPtr matFile, ref int num);
public static string[] matGetDir(IntPtr matFile)
{
// Obtain as IntPtr
var count = 0;
var pointers = matGetDir(matFile, ref count);
// Handling errors as noticed by David Heffernan
if (count < 0) { throw new Exception("Failed to obtain list of variables in selected mat file."); }
if (pointers == IntPtr.Zero) { return new string[0]; }
// Cast into IntPtr[]
var ptrs = new IntPtr[count];
Marshal.Copy(pointers, ptrs, 0, count);
// Convert each value in IntPtr[] into string
// NB: using System.Linq;
var strs = ptrs.Select(x => Marshal.PtrToStringAnsi(x)).ToArray();
// Don't forget to free memory allocated by Matlab
// NB: Deleting global pointer only ==> see "edit([matlabroot '/extern/examples/eng_mat/matdgns.c']);" example
mxFree(pointers);
// And voilà
return strs;
}
因此,只需像您最初尝试的那样在私有方法中转换为 IntPtr
,然后添加一个 public 方法以转换为 string[]
。有关详细信息,请参阅代码中的注释。
您无法告诉 p/invoke 编组器如何为您编组它。你需要手动完成。关于您的问题,有趣的一件事是您没有提供任何有关此 char**
真正含义的详细信息。了解类型并没有完全定义参数的语义对您来说非常重要,或者在这种情况下是 return 值。
我们很容易猜到,但最好在文档中查找:http://uk.mathworks.com/help/matlab/apiref/matgetdir.html
Arguments
mfp
Pointer to MAT-file information
num
Pointer to the variable containing the number of mxArrays in the
MAT-file
Returns
Pointer to an internal array containing pointers to the names of the
mxArrays in the MAT-file pointed to by mfp. In C, each name is a
NULL-terminated string. The num output argument is the length of the
internal array (number of mxArrays in the MAT-file). If num is zero,
mfp contains no arrays.
matGetDir returns NULL in C (0 in Fortran). If matGetDir fails, sets
num to a negative number.
所以,我们这样声明 p/invoke:
[DllImport("libmat.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr matGetDir(IntPtr matFile, out int num);
这样称呼它:
int num;
IntPtr matFile = ...;
IntPtr namesPtr = matGetDir(mayFile, out num);
if (names == IntPtr.Zero)
// handle error
string[] names = new string[num];
for (int i = 0; i < num; i++)
{
int offset = i * Marshal.SizeOf(typeof(IntPtr));
names[i] = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(namesPtr, offset));
}
我的问题是我正在使用 C# 中的 MATLAB API,这是给我带来麻烦的函数。
C代码:
EXTERN_C char ** matGetDir(MATFile * pMF, int *num);
我希望它能工作,但不幸的是它没有(C# 代码):
[DllImport("libmat.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern string[] matGetDir(IntPtr matFile, ref int num);
如果我用 IntPtr
而不是 string[]
我可以调用函数,但是我不知道如何将代码从 IntPtr
转换为 string[]
编辑 1:
我也尝试使用这些属性,但它失败并出现错误:无法封送 'return value':无效的 managed/unmanaged 类型组合。
[return: MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)]
private static extern string[] matGetDir(IntPtr matFile, ref int num);
你可以这样做:
[DllImport("libmx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void mxFree(IntPtr ptr);
[DllImport("libmat.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern IntPtr matGetDir(IntPtr matFile, ref int num);
public static string[] matGetDir(IntPtr matFile)
{
// Obtain as IntPtr
var count = 0;
var pointers = matGetDir(matFile, ref count);
// Handling errors as noticed by David Heffernan
if (count < 0) { throw new Exception("Failed to obtain list of variables in selected mat file."); }
if (pointers == IntPtr.Zero) { return new string[0]; }
// Cast into IntPtr[]
var ptrs = new IntPtr[count];
Marshal.Copy(pointers, ptrs, 0, count);
// Convert each value in IntPtr[] into string
// NB: using System.Linq;
var strs = ptrs.Select(x => Marshal.PtrToStringAnsi(x)).ToArray();
// Don't forget to free memory allocated by Matlab
// NB: Deleting global pointer only ==> see "edit([matlabroot '/extern/examples/eng_mat/matdgns.c']);" example
mxFree(pointers);
// And voilà
return strs;
}
因此,只需像您最初尝试的那样在私有方法中转换为 IntPtr
,然后添加一个 public 方法以转换为 string[]
。有关详细信息,请参阅代码中的注释。
您无法告诉 p/invoke 编组器如何为您编组它。你需要手动完成。关于您的问题,有趣的一件事是您没有提供任何有关此 char**
真正含义的详细信息。了解类型并没有完全定义参数的语义对您来说非常重要,或者在这种情况下是 return 值。
我们很容易猜到,但最好在文档中查找:http://uk.mathworks.com/help/matlab/apiref/matgetdir.html
Arguments
mfp
Pointer to MAT-file information
num
Pointer to the variable containing the number of mxArrays in the MAT-file
Returns
Pointer to an internal array containing pointers to the names of the mxArrays in the MAT-file pointed to by mfp. In C, each name is a NULL-terminated string. The num output argument is the length of the internal array (number of mxArrays in the MAT-file). If num is zero, mfp contains no arrays.
matGetDir returns NULL in C (0 in Fortran). If matGetDir fails, sets num to a negative number.
所以,我们这样声明 p/invoke:
[DllImport("libmat.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr matGetDir(IntPtr matFile, out int num);
这样称呼它:
int num;
IntPtr matFile = ...;
IntPtr namesPtr = matGetDir(mayFile, out num);
if (names == IntPtr.Zero)
// handle error
string[] names = new string[num];
for (int i = 0; i < num; i++)
{
int offset = i * Marshal.SizeOf(typeof(IntPtr));
names[i] = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(namesPtr, offset));
}