如何调用C#委托以本机C最简单的方式传递字符串数组? [英] How to call C# delegate to pass array of strings from native C simplest way?

查看:127
本文介绍了如何调用C#委托以本机C最简单的方式传递字符串数组?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道这可以通过以下方式来实现:在C中进行malloc分配,将malloced指针传递给参数类型为IntPtr的委托,编组为string [],然后使用托管代码中单独的,导出的C函数释放malloc的内存.

I know this can be done by mallocing in C, passing malloced pointer to delegate with parameter type IntPtr, marshalling to string[] and then freeing malloced memory with separate, exported C-function from managed code.

我的问题是:这可以做得更简单吗?例如. :

My question is: Can this be done simpler way? E.g. :

  • C#委托参数的类型为字符串[]?
  • 没有单独的免费函数可以从托管代码中调用

我尝试使用委托人签名:

I tried with delegate signature:

[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);

这导致我只能在数组中使用第一个字符串.

This results in that i can use only 1st string in array.

推荐答案

这里是正确执行操作的方法,我将举一个完整的示例,使其具有可复制性.

Here's how to do it properly, I'll give a full example so it's reproductible.

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);
}

这里没什么好想的,我只是添加了必要的胶水代码,其余的都留下来了.

Nothing fancy here, I just added the necessary glue code and left the rest alone.

[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封送拆收器以下内容:

The MarshalAs attribute tells the .NET marshaler the following:

  • UnmanagedType.LPArray您正在获取数组...
  • ArraySubType = UnmanagedType.LPStr ...标准C字符串...
  • SizeParamIndex = 1 ...并且该数组的大小由第二个参数指定.
  • UnmanagedType.LPArray You're getting an array...
  • ArraySubType = UnmanagedType.LPStr ...of standard C strings...
  • SizeParamIndex = 1 ...and the size of that array is specified by the second parameter.

在调用C#方法之前,.NET封送程序将C字符串复制并转换为System.String实例.因此,如果您需要将动态生成的字符串传递给C#,请对其进行malloc调用,然后调用gSetStringValuesCB,然后您就可以立即从所有C代码中进行free,因为.NET具有自己的副本.数据.

The C strings are copied and converted to System.String instances by the .NET marshaler before the invocation of your C# method. So if you need to pass dynamically generated strings to C#, you malloc them, then you call gSetStringValuesCB, and you can free them immediately afterwards, all from the C code, as .NET has its own copy of the data.

您可以参考文档 :

UnmanagedType.LPArray:

指向C样式数组的第一个元素的指针.从托管代码编组到非托管代码时,数组的长度由托管数组的长度确定. 从非托管代码到托管代码封送处理时,数组的长度由MarshalAsAttribute.SizeConstMarshalAsAttribute.SizeParamIndex字段确定,并在需要时由数组中元素的非托管类型决定(可选)区分字符串类型.

A pointer to the first element of a C-style array. When marshaling from managed to unmanaged code, the length of the array is determined by the length of the managed array. When marshaling from unmanaged to managed code, the length of the array is determined from the MarshalAsAttribute.SizeConst and MarshalAsAttribute.SizeParamIndex fields, optionally followed by the unmanaged type of the elements within the array when it is necessary to differentiate among string types.

UnmanagedType.LPStr:

一个单字节,以空字符结尾的ANSI字符串.您可以在System.StringSystem.Text.StringBuilder数据类型上使用此成员.

A single byte, null-terminated ANSI character string. You can use this member on the System.String and System.Text.StringBuilder data types.

MarshalAs.SizeParamIndex:

指示从零开始的参数,该参数包含数组元素的数量,类似于COM中的size_is.

这篇关于如何调用C#委托以本机C最简单的方式传递字符串数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆