使用DllImport将返回的char *从C ++传递到C#

问题描述 投票:0回答:2

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

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

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

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

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

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

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

    #define DLL_EXPORT extern "C" __declspec(dllexport)
    char* getRbtData = nullptr;
    DLL_EXPORT char* func_getRbtData();
    
  • Dlltest.cpp中的C ++代码:

    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#代码:

    [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# c++ return dllimport
2个回答
0
投票

[new char(128)返回一个指向一个字符的指针,初始值为128。

我可以告诉您如何分配128个字符,但是主要的问题是您无法清理它,所以这不是一个有用的答案。检查有关将字符串返回C#的现有问题。


0
投票

您的代码有几个问题。

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

  • getRbtData = new char(128);

    您正在分配值为128的single char,而不是128chars的array。为此,您需要使用new char[128]

  • memset(getRbtData, 0, strlen(getRbtData));

    getRbtData不是指向以空值结尾的字符串的指针,所以strlen(getRbtData)未定义的行为。它在计算长度的同时读入周围的存储器,直到在存储器中找到随机的0x00字节为止。然后,随后的memset()getRbtData将覆盖周围的内存。如果它不只是完全崩溃。

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

    在C ++ 11及更高版本中,此分配实际上是非法的,不会编译。字符串文字是const数据,因此在指针类型中需要使用const char而不是char。即使在较旧的编译器中,您也应该这样做。

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

    strlen(_getRbtData)是可以的,因为_getRbtData是指向以空字符结尾的字符串的指针。但是,由于没有为getRbtData分配足够的内存来接收所有复制的char,因此memcpy()中的getRbtData也是未定义的行为,并且如果没有完全崩溃,则会浪费内存。

  • return getRbtData;

    这可以将指针传递给C#。但是,由于内存是用new(更好是new[])分配的,因此需要用deletedelete[])释放它,而您没有这样做。因此,您正在泄漏内存。 C#端的Marshal.PtrToStringAnsi()不会(也不能)为您释放new的内存。因此,您的C#代码将需要将指针传递回DLL,以便它可以正确delete内存。

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

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

Dlltest.h

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

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];
    memcpy(getRbtData, _getRbtData, len+1);
    return getRbtData;
}

void func_getRbtData(char *p)
{
    delete[] p;
}

UITest.xaml.cs

[DllImport("DllTest.dll", EntryPoint = "func_getRbtData", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr func_getRbtData();

[DllImport("DllTest.dll", EntryPoint = "func_freeRbtData", CharSet = CharSet.Ansi, 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);
    words = str.Split(';');
    lb_content.Content = words[1];
}
© www.soinside.com 2019 - 2024. All rights reserved.