C# 中的元帅 C++“char**”

Posted

技术标签:

【中文标题】C# 中的元帅 C++“char**”【英文标题】:Marshal C++ "char**" in C# 【发布时间】:2011-06-24 17:20:45 【问题描述】:

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

C#代码:

[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.

我应该怎么做?

【问题讨论】:

【参考方案1】:

您声明了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,因为有明确的长度声明,并在C++ 中使用BSTR helpers 对其进行操作。

【讨论】:

好的,我尝试了'UnmanagedType.LPStr'并且c#打印了正确的值,但之后我得到错误:'访问冲突读取位置0xbd9c6242。'我是否还需要更改 C++ 代码中的某些内容? 我想我需要把 [in, out] 放在某个地方,但我现在不知道在哪里。在链接的示例中,这是在接口声明中。由于我是在另一个方向调用过程,所以我没有接口...... 您的代码中仍有大量错误。 (MyProc)(&amp;s3) 传入char***char** 的地址),而不是预期的char**。您使用基本类型 (char*),而无需跨调用边界进行任何适当的内存管理。正如我所说,使用BSTR,在C# 端它们将被自动管理。在 C++ 方面,您需要正确释放它们。 为什么要加载裸DLL并调用GetProcAddress?为什么不采用正确支持的 COM Interrop 方式,向 COM 注册您的托管程序集并从 COM 用 C++ 调用它?你甚至可以免费注册 COM msdn.microsoft.com/en-us/library/ms973913.aspx 跨越托管/非托管边界的指针时,您必须使用适当的内存管理,例如CoTaskMemAlloc,因为互操作封送器将使用CoTaskMemFree 分配传入的内存,请参阅msdn.microsoft.com/en-us/magazine/cc164193.aspx#S6。传入堆栈变量地址(如&amp;s1&amp;s2 等)将导致损坏和访问冲突。【参考方案2】:

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

【讨论】:

我更改了它并更新了问题,因为它现在仍在工作。现在可以看看吗?【参考方案3】:

这行得通吗?

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

【讨论】:

【参考方案4】:

在许多情况下,似乎是这里的情况(请参阅 Remus Rusanu 接受的答案),在 API 声明中使用正确的编组属性是拥有互操作“胶水”所需要的一切” 在界面的两端开始“互相玩”并导致......

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

我在原始帖子发布 5 个月后添加了这个答案,因为这个问题及其回复对于修复我最近的互操作错误非常有用,但没有直接提及有关许多互操作问题的非常合理的原因的信息,即:

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

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

[InAttribute] 和 [OutAttribute] 又名 [In] 和 [Out] 关键字 OutRef 并通过引用传递 返回值 StringBuilder 和封送处理 复制和固定 (固定 = 当 CLR 认为可以安全地锁定 CLR 堆中的数据区域并将相应的指针传递给非托管代码时,由 CLR 进行优化) 内存所有权: 不允许更改与就地更改与参考更改 此外,需要使用 CoTaskMemFree()CoTaskMemAlloc() 来释放或分配分别从或发送到托管代码。 反向 P/Invoke 和委托生命周期 P/Invoke 互操作助手

这篇文章非常有用,因为它以单一文档和可访问的方式收集信息,否则这些信息会散布在十几个技术权威但枯燥[而且经常令人困惑的]参考文档中(参见old but on-point joke about Microsoft's documentation at large)。 简而言之,它对于互操作解决方案的临时实施者来说是一本很好的入门或复习

【讨论】:

自从笑话链接失效后,这里给它一个way-back link。如果这个链接最终也失效了,那就是微软/直升机的笑话。

以上是关于C# 中的元帅 C++“char**”的主要内容,如果未能解决你的问题,请参考以下文章

C# 中的元帅“char *”

从 c# 到 c++ 的结构中的元帅结构

在 C# 中元帅 C++ wchar_t**

具有对 C# 的整数引用的元帅结构

VS2010中的元帅结构指针

如何将结构中的char *从c#传递到c++