我知道这可以通过在C中进行mallocing,将malloced指针传递给参数类型为IntPtr的委托,将编组传递给string [],然后使用托管代码中单独的导出C函数释放malloced内存来完成。
我的问题是:这可以更简单的方式吗?例如。 :
编辑:我尝试过代理签名:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
MyManagedDelegate(string[] values, int valueCount)
和C中的功能:
void NativeCallDelegate(char *pStringValues[], int nValues)
{
if (gSetStringValuesCB)
gSetStringValuesCB(pStringValues, nValues);
}
在C中调用它:
char *Values[]= {"One", "Two", "Three"};
NativeCallDelegate(Values, 3);
这导致我只能使用数组中的第一个字符串。
答案 0 :(得分:7)
以下是如何正确完成,我将给出一个完整的示例,以便它可以重现。
typedef void(*setStringValuesCB_t)(char *pStringValues[], int nValues);
static setStringValuesCB_t gSetStringValuesCB;
void NativeCallDelegate(char *pStringValues[], int nValues)
{
if (gSetStringValuesCB)
gSetStringValuesCB(pStringValues, nValues);
}
__declspec(dllexport) void NativeLibCall(setStringValuesCB_t callback)
{
gSetStringValuesCB = callback;
char *Values[] = { "One", "Two", "Three" };
NativeCallDelegate(Values, 3);
}
这里没什么好看的,我只是添加了必要的胶水代码,剩下的就是其余的。
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MyManagedDelegate(
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr, SizeParamIndex = 1)]
string[] values,
int valueCount);
[DllImport("NativeTemp", CallingConvention = CallingConvention.Cdecl)]
public static extern void NativeLibCall(MyManagedDelegate callback);
public static void Main()
{
NativeLibCall(PrintReceivedData);
}
public static void PrintReceivedData(string[] values, int valueCount)
{
foreach (var item in values)
Console.WriteLine(item);
}
诀窍在于编组部分:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MyManagedDelegate(
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr, SizeParamIndex = 1)]
string[] values,
int valueCount);
MarshalAs
属性告诉.NET编组程序:
UnmanagedType.LPArray
你得到一个数组...... ArraySubType = UnmanagedType.LPStr
...标准C字符串...... SizeParamIndex = 1
...并且该数组的大小由第二个参数指定。在调用C#方法之前,.NET编组器会复制C字符串并将其转换为System.String
实例。因此,如果您需要将动态生成的字符串传递给C#,您malloc
,然后调用gSetStringValuesCB
,然后您可以立即free
,所有这些都来自C代码,如.NET有自己的数据副本。
您可以参考the docs:
UnmanagedType.LPArray
:指向C样式数组的第一个元素的指针。从托管代码编组到非托管代码时,阵列的长度由托管阵列的长度决定。 从非托管代码封送到托管代码时,数组的长度由
MarshalAsAttribute.SizeConst
和MarshalAsAttribute.SizeParamIndex
字段确定,可选地后跟数组中元素的非托管类型有必要区分字符串类型。
UnmanagedType.LPStr
:单字节,以空值终止的ANSI字符串。您可以在
System.String
和System.Text.StringBuilder
数据类型上使用此成员。
MarshalAs.SizeParamIndex
:表示包含数组元素计数的从零开始的参数,类似于COM中的
size_is
。
答案 1 :(得分:-1)
我提出的远非最佳解决方案:
public delegate void MyManagedDelegate([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr, SizeConst=10)]string[] values, int valueCount);
如果这样调用,则无效:
char *Values[]= {"One", "Two", "Three"};
NativeCallDelegate(Values, 3);
我可以使用固定大小为10的数组来复制值,并始终传递给委托。这不是我想要的。我想知道这是否有好的解决方案...