c#中的marshal c ++“char **”

时间:2011-06-24 17:20:46

标签: c# c++ marshalling dynamic-linking

我从C ++调用C#方法并将char **作为参数传递。它必须是char **,因为我需要通过参数返回值。

c#c​​ode:

    [ExportDll("test",
        System.Runtime.InteropServices.CallingConvention.StdCall)]

    public static int test([MarshalAs(UnmanagedType.AnsiBStr)] ref string p)
    {
        Console.WriteLine(p);
    }

用于调用函数的c ++代码:

typedef int (__stdcall *MYPROC)(char **); 

VOID main(VOID) 
{ 
HINSTANCE hinstLib; 
MYPROC MyProc; 
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE; 

hinstLib = LoadLibrary(TEXT("mydll.dll")); 

if (hinstLib != NULL) 
{ 
    ProcAdd = (MYPROC) GetProcAddress(hinstLib, "test"); 

    if (NULL != ProcAdd) 
    {
        fRunTimeLinkSuccess = TRUE;
        char s1[] = "test"; 
        char *s2 = s1;
        char **s3 = &s2;

        (MyProc) (s3); 
        cout << s3;
    }

    fFreeResult = FreeLibrary(hinstLib); 
} 
}

传递char *(在c#中删除ref,在c ++中使用char *)很简单,但在尝试传递char **时,我在线上调用函数时遇到运行时错误:(

在c#中,Console.WriteLine打印出正确的值,但在那之后,我收到一个错误:

Windows has triggered a breakpoint in COMDynamicLoad.exe.

This may be due to a corruption of the heap, which indicates a bug in COMDynamicLoad.exe or any of the DLLs it has loaded.

This may also be due to the user pressing F12 while COMDynamicLoad.exe has focus.

The output window may have more diagnostic information.

我该怎么做?

4 个答案:

答案 0 :(得分:3)

这可能是因为您使用char*指向字符串文字 - 这很糟糕,因为修改该内存是未定义的行为。

答案 1 :(得分:3)

您声明ref UnmanagedType.AnsiBStr,但您希望char**。这不起作用,因为对BSTR的引用不是char **。有关封送声明的示例,请参阅Default Marshaling for Strings。这些是输入输出字符串的可能声明:

PassStringRef2([in, out] BSTR *s);
PassStringRef3([in, out] LPStr *s);
PassStringRef4([in, out] LPWStr *s);

和等效的C#编组声明是:

PassStringRef2([MarshalAs(UnmanagedType.BStr)]ref String s);
PassStringRef3([MarshalAs(UnmanagedType.LPStr)]ref String s);
PassStringRef4([MarshalAs(UnmanagedType.LPWStr)]ref String s);

您的char**声明相当于LPStr *s,因此正确的封送处理是[MarshalAs(UnmanagedType.LPStr)]ref String s。但更好的选择是使用BSTR因为显式长度声明,并使用BSTR helpers在C ++中对其进行操作。

答案 2 :(得分:0)

这会有用吗?

public static int test([In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr)] string[] p)

答案 3 :(得分:0)

在许多情况下,看起来就像这里的情况(参见 Remus Rusanu 的接受回答),在API声明中使用正确的编组属性就是使用互操作“粘合剂所需的全部内容” “在界面的两端开始”彼此玩得很好“并导致......

  • 发送/发送的正确数据值[good!]
  • Windows不会触发各种疑似的自动断点... corruption of the heap ... [更好! ; - )]

我在原帖后5个月添加了这个答案,因为这个问题及其答案在修复我最近的互操作错误方面非常有用,但没有直接提到有关许多互操作问题的可能原因的信息,即:

内存所有权惯例不匹配
(和/或在内存分配和释放方法中)。

January 2008 article on MSDN Magazine标题为托管和非托管代码之间的封送为我提供了有关内存所有权约定所需的信息。实际上,本文提供了编组过程的完整概述。它涵盖了

的具体细节和示例
  • [InAttribute]和[OutAttribute]又名[ In ]和[ Out ]
  • 关键字 Out 参考 并通过引用传递
  • 返回值
  • StringBuilder 和编组
  • 复制并 固定
    (当它认为可以安全地锁定CLR堆中的数据区并将相应的指针传递给非托管代码时,由CLR进行固定=最优化)
  • 内存所有权
    不允许更改与就地更改与参考更改
    此外,还需要使用 CoTaskMemFree() CoTaskMemAlloc() 来释放或分配从或发送到托管代码。
  • 反向P / Invoke 并委派生命周期
  • P / Invoke Interop Assistant

这篇文章非常有用,因为它收集了一份文件,并以一种易于获取的方式提供信息,这些信息在十几个技术上权威但干燥[和经常“令人困惑的]参考文件中传播(参见old but on-point joke about Microsoft's documentation at large)。<登记/> 简而言之,它使得成为互操作解决方案的偶然实施者的良好入门或复习