2

我需要将一组 C# 字符串传递给 C 代码

示例 C 代码

void print_string_array(const char** str_array, int length){
    for (int i = 0; i < length; ++i) {
        printf("Sting[%l] = %s\n", i, str_array[i]);
    }
}

我尝试过的 C#(这不起作用)

string foo[] = {"testing", "one", "two", "three"};  
print_string_array(foo, foo.Length);

[DllImport(my_C_dll, CharSet = CharSet.Ansi)]
private static extern void print_string_array([In][MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr)] string[] sa, int length);

失败并出现 System.AccessViolationException System.AccessViolationException :尝试读取或写入受保护的内存。这通常表明其他内存已损坏。

我也试过(这也没用)

string[] foo = {"testing", "one", "two", "three"};  
IntPtr[] s_array = new IntPtr[foo.Length];

for(int i = 0; i < foo.Length; ++i) 
{
    s_array[i] = Marshal.StringToCoTaskMemAnsi(foo[i])
}
print_string_array( s_array, s_array.Length);

[DllImport(my_C_dll, CharSet = CharSet.Ansi)]
private static extern void print_string_array(IntPtr[] sa, int length);

这也失败了

System.AccessViolationException
System.AccessViolationException : Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

有人知道如何将字符串数组从 c# 传递到 C 吗?


更新:根据 David Heffernan 的建议添加了错误消息。在 C 代码中将 size_t 更改为 int ,因为它不会影响我尝试做的事情。仍然得到相同的错误。

4

2 回答 2

7

您可以像这样在 C# 中简单地声明您的函数:

[DllImport(my_C_dll, CallingConvention=CallingConvention.Cdecl)]
static extern void print_string_array([In] string[] str_array, IntPtr length);

在编写时,您的 C++ 代码似乎使用cdecl调用约定。因此,您可能需要使 C# 声明匹配。我怀疑这是您面临的主要问题。

另请注意size_t,您用于length参数的 32 位进程中为 32 位宽,而 64 位进程中为 64 位宽。所以正确的 C# 类型是IntPtr. 就我个人而言,我会声明它同时出现int在 C++ 和 C# 代码中。


最后一句忠告。当您遇到失败时,请在您的问题中包含错误消息。我怀疑您的第一个代码因此错误而失败:

调用 PInvoke 函数 'MyApp!MyApp.Program::print_string_array' 使堆栈失衡。

如果您将其包含在问题中,那将是一个很大的帮助。

于 2012-11-10T19:29:25.167 回答
1

您的第二次尝试非常接近。尝试以下操作:

string[] foo = {"testing", "one", "two", "three"};  
IntPtr[] s_array = new IntPtr[foo.Length];

for(int i = 0; i < foo.Length; ++i) 
{
    s_array[i] = Marshal.StringToHGlobalAnsi(foo[i])
}
GCHandle gH = GCHandle.Alloc(s_array, GCHandleType.Pinned);
print_string_array( gH.AddrOfPinnedObject(), s_array.Length);
gH.Free();
for(int i = 0; i < foo.Length; ++i) 
{
    Marshal.FreeHGlobal(s_array[i])
}

[DllImport(my_C_dll, CharSet = CharSet.Ansi)]
private static extern int print_string_array(IntPtr sa, int length);
于 2012-11-10T18:08:04.470 回答