使用DllImport将返回的char *从C ++传递到C# [英] Passing a return char* from C++ to C# with DllImport

查看:284
本文介绍了使用DllImport将返回的char *从C ++传递到C#的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是C#WPF和C ++的新手.

I am a newbie on both C# WPF and C++.

最近,我得到了一个外部.dll,它返回一个 char * ,并且我想使用 DllImport 来接收C#中的返回值.然后,使用 str.Split(';')分隔字符.为此,我创建了一个按钮,以显示单击按钮时在标签上分割的字符串中的第一个字符.

Recently, I got an external .dll, which returns a char*, and I want to receive the return value in C# by using DllImport. Then, using str.Split(';') to separate the characters. To the purpose, I created a button to show the first character in the string I split on a label when I click the button.

因此,我使用 IntPtr 接收来自C ++ .dll的 char * ,然后调用 Marshal.PtrToStringAnsi()把它变成一个字符串.但是,当我执行代码时,它有时会起作用,但有时会崩溃.然后错误代码总是显示

Therefore, I use an IntPtr to receive a char*, which is from C++ .dll, and call Marshal.PtrToStringAnsi() to turn it into a string. However, when I execute the code, it sometimes works but sometimes crushes. Then error code always shows

Unhandled exception at 0x00007FFC06839269 (ntdll.dll) in UITest.exe: 0xC0000374: heap corruption  (parameters: 0x00007FFC068A27F0).

我认为我的代码合理,找不到根本原因.谁能帮我?谢谢!

I thought my code is reasonable and I couldn't find root causes. Can anyone help me? Thanks!

下面显示了.dll中的内容以及我在C#中使用的代码.

The below shows the content in .dll and also the code I used in C#.

    Dlitest.h中的
  • C ++代码:

  • C++ code in Dlltest.h:

#define DLL_EXPORT extern "C" __declspec(dllexport)
char* getRbtData = nullptr;
DLL_EXPORT char* func_getRbtData();

Dlitest.cpp中的

  • C ++代码:

  • C++ code in Dlltest.cpp:

    char* func_getRbtData()
    {
       getRbtData = new char(128);
       memset(getRbtData, 0, strlen(getRbtData));
       char* _getRbtData = "1.0;23.0;55.0;91.0;594.0;";
       memcpy(getRbtData, _getRbtData, strlen(_getRbtData));
       return getRbtData;
    };
    

  • UITest.xaml.cs中的
  • C#代码:

  • C# code in UITest.xaml.cs:

    [DllImport("DllTest.dll",EntryPoint = "func_getRbtData", CharSet = CharSet.Ansi)]
    
    public static extern IntPtr func_getRbtData();
    
    string[] words;
    private void btn_test_Click(object sender, RoutedEventArgs e)
    {
        IntPtr intptr = func_getRbtData();
        string str = Marshal.PtrToStringAnsi(intptr);
        words = str.Split(';');
        lb_content.Content = words[1];
    }
    

  • 推荐答案

    您的代码有几个问题.

    在C ++端,您的DLL函数实现完全错误:

    On the C++ side, your DLL function is implemented all wrong:

    • getRbtData = new char(128);

    您要分配的单个 char 值为128,而不是128个 char array .为此,您需要使用 new char [128] .

    You are allocating a single char whose value is 128, not an array of 128 chars. You need to use new char[128] instead for that.

    内存集(getRbtData,0,strlen(getRbtData));

    getRbtData 不是指向以null终止的字符串的指针,因此 strlen(getRbtData)未定义的行为.它会在计算长度的同时读入周围的内存,直到在内存中找到一个随机的0x00字节为止.

    getRbtData is not a pointer to a null-terminated string, so strlen(getRbtData) is undefined behavior. It reads into surrounding memory while calculating the length until it finds a random 0x00 byte in memory.

    ,然后到 getRbtData 中的后续 memset()将覆盖周围的内存.如果不是完全崩溃的话.

    And then the subsequent memset() into getRbtData will overwrite that surrounding memory. If it doesn't just crash outright.

    char * _getRbtData ="1.0; 23.0; 55.0; 91.0; 594.0;";

    在C ++ 11之前,此分配是可以的,但不建议这样做.在C ++ 11和更高版本中,此分配实际上是非法的,不会编译.

    Prior to C++11, this assignment is OK but discouraged. In C++11 and later, this assignment is actually illegal and won't compile.

    字符串文字是只读数据,因此您需要在指针类型中使用 const char 而不是 char .即使在较旧的编译器中,您也应该这样做.

    String literals are read-only data, so you need to use const char instead of char in your pointer type. You should do that even in older compilers.

    memcpy(getRbtData,_getRbtData,strlen(_getRbtData));

    strlen(_getRbtData)是可以的,因为 _getRbtData 是指向以空字符结尾的字符串的指针.

    strlen(_getRbtData) is OK since _getRbtData is a pointer to a null-terminated string.

    但是,由于未为 getRbtData 分配足够的内存来接收所有复制的 char s,因此 memcpy()进入getRbtData 也是未定义的行为,如果不彻底崩溃,则会浪费内存.

    However, since getRbtData is not allocated with enough memory to receive all of the copied chars, memcpy() into getRbtData is also undefined behavior and will trash memory, if not crash outright.

    返回getRbtData;

    可以将指针传递给C#.

    This is OK to pass the pointer to C#.

    但是,由于内存是通过 new (最好是 new [] )分配的,因此需要通过 delete 释放它( delete [] ),您没有这样做.因此,您正在泄漏内存.

    However, since the memory is being allocated with new (better, new[]), it needs to be freed with delete (delete[]), which you are not doing. So you are leaking the memory.

    Marshal.PtrToStringAnsi()不会(也不能)为您释放 new 的内存.因此,您的C#代码将需要将指针传递回DLL,以便它可以正确地删除内存.

    Marshal.PtrToStringAnsi() on the C# side will not (and cannot) free new'ed memory for you. So your C# code will need to pass the pointer back to the DLL so it can delete the memory properly.

    否则,您将需要使用Win32 API LocalAlloc() CoTaskMemAlloc()函数分配内存,以便使 Marshal 类可以在C#端使用它直接释放内存,而无需将其传递回DLL.

    Otherwise, you will need to allocate the memory using the Win32 API LocalAlloc() or CoTaskMemAlloc() function so that the Marshal class can be used on the C# side to free the memory directly without passing it back to the DLL at all.

    在C#端,您在 DllImport 语句上使用了错误的调用约定.为了与大多数Win32 API函数兼容,默认值为 StdCall .但是您的DLL函数根本没有指定任何调用约定.除非配置不同,否则大多数C/C ++编译器将默认为 __ cdecl .

    On the C# side, you are using the wrong calling convention on your DllImport statement. The default is StdCall for compatibility with most Win32 API functions. But your DLL function is not specifying any calling convention at all. Most C/C++ compilers will default to __cdecl unless configured differently.

    话虽如此,请尝试以下方法:

    With that said, try this instead:

    Dlltest.h

    Dlltest.h

    #define DLL_EXPORT extern "C" __declspec(dllexport)
    DLL_EXPORT char* func_getRbtData();
    DLL_EXPORT void func_freeRbtData(char*);
    

    Dlltest.cpp

    Dlltest.cpp

    char* func_getRbtData()
    {
        const char* _getRbtData = "1.0;23.0;55.0;91.0;594.0;";
        int len = strlen(_getRbtData);
        char *getRbtData = new char[len+1];
        // alternatively:
        /*
        char *getRbtData = (char*) LocalAlloc(LMEM_FIXED, len+1);
        if (!getRbtData) return NULL;
        */
        memcpy(getRbtData, _getRbtData, len+1);
        return getRbtData;
    }
    
    void func_freeRbtData(char *p)
    {
        delete[] p;
        // alternatively:
        // LocalFree((HLOCAL)p);
    }
    

    UITest.xaml.cs

    UITest.xaml.cs

    [DllImport("DllTest.dll", EntryPoint = "func_getRbtData", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr func_getRbtData();
    
    [DllImport("DllTest.dll", EntryPoint = "func_freeRbtData", CallingConvention = CallingConvention.Cdecl)]
    public static extern void func_freeRbtData(IntPtr p);
    
    string[] words;
    private void btn_test_Click(object sender, RoutedEventArgs e)
    {
        IntPtr intptr = func_getRbtData();
        string str = Marshal.PtrToStringAnsi(intptr);
        func_freeRbtData(intptr);
        // alternatively:
        // Marshal.FreeHGlobal(intptr);
        words = str.Split(';');
        lb_content.Content = words[1];
    }
    

    这篇关于使用DllImport将返回的char *从C ++传递到C#的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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