【14】C#实战篇——C++动态库dll 接口函数将char* strErr字符串 传给C# ,并且在winform的MessageBox和listbox中显示。C++ string 日志传给 C#

文章目录

  • 1 通过retun返回 字符数组
    • 1.1 字符数组char strErr[] --- 失败
    • 1.2 问题分析及解决---成功
    • 1.3 问题分析及解决---成功
  • 2 通过retun返回 字符串常量
    • 2.1 字符串常量 char* strErr --- 成功
  • 3 将char* strErr字符串 和 int合并传给C# ---成功
  • 4 C++ string strErr传给 C# ---失败
    • 4.1 str.c_str()将字符串转为C语言风格的字符串---失败
    • 4.2 问题分析及解决---成功
  • 5 通过参数传递 字符串
    • 5.1 字符串常量 char* strErr参数传递----失败
    • 5.2 问题分析及解决---成功

1 通过retun返回 字符数组

1.1 字符数组char strErr[] --- 失败

例如:char strErr[] = "读取模板图像 失败 !"传给C# ,并且在winform的MessageBox和listbox中显示

将C语言中的字符数组 strErr[] 传递给C#,并在WinForm的MessageBox和ListBox中显示,你可以创建一个C#的Interop接口来调用C语言的函数,然后将返回的字符串显示在MessageBox和ListBox中。

C语言代码(C.dll):

cpp 复制代码
__declspec(dllexport) char* strTest()
{
  // 字符数组
	char strErr[] = "读取模板图像 失败 !";
	return strErr;
}

C#调用:

cpp 复制代码
        private void button1_Click(object sender, EventArgs e)
        {        
            // 调用C函数获取字符串
            IntPtr strPtr = strTest();

            string strErr = Marshal.PtrToStringAnsi(strPtr);

            // 在MessageBox中显示
            MessageBox.Show(strErr,"Error");

            // 在ListBox中显示
            listBox1.Items.Add(strErr);
        }

1.2 问题分析及解决---成功

strErr 是一个在栈上分配的字符数组,它的生命周期仅限于函数调用期间 。一旦函数返回,该数组将被销毁,指向它的指针将变得无效。

因此,在C#中接收到的 strErr 实际上指向了已经被销毁的内存区域,这就解释了为什么会出现乱码。

要解决这个问题,你可以在C语言中动态分配内存,并返回一个指向这块内存的指针。

cpp 复制代码
char* strTest4()
{
	char strErr[] = "读取模板图像 失败 !";

	// 使用动态内存分配函数分配足够的空间
	char *c_strErr = (char*)malloc(50 * sizeof(char)); // 假设字符串长度不超过 50

	// 将字符串复制到动态分配的内存中
	strcpy(c_strErr, strErr);

	return c_strErr;
}

我们使用 malloc 函数来动态分配足够的内存给 C 风格字符串,确保在函数返回后依然有效。然后使用 strcpy 将字符串复制到这个内存块中。在使用完 C 风格字符串后,记得释放它所占用的内存:

cpp 复制代码
extern "C" __declspec(dllexport) void freeStr(const char* str)
{
	delete[] str;
}

C#调用

cpp 复制代码
        [DllImport("LineProApi.dll", EntryPoint = "strTest", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr strTest();

        [DllImport("LineProApi.dll", EntryPoint = "freeStr", CallingConvention = CallingConvention.Cdecl)]
        public static extern void freeStr(IntPtr str);



        private void button1_Click(object sender, EventArgs e)
        {
            //// 调用C函数获取字符串
            IntPtr strPtr = strTest();
            string strErr = Marshal.PtrToStringAnsi(strPtr);

            // 在MessageBox中显示
            MessageBox.Show(strErr,"Error");

            // 在ListBox中显示
            listBox1.Items.Add(strErr);

            // 释放C风格字符串的内存
            freeStr(strPtr);
        }

1.3 问题分析及解决---成功

strErr 是一个在栈上分配的字符数组,它的生命周期仅限于函数调用期间 。一旦函数返回,该数组将被销毁,指向它的指针将变得无效。

  • 解决1 C语言中动态分配内存,并返回一个指向这块内存的指针。

  • 解决2 定义一个静态的字符数组strErr ,它在函数内部被定义,在程序的整个生命周期内都是有效的;(不会在函数退出时被销毁,也无需要手动释放 strErr。)

cpp 复制代码
char* strToCSharp()
{
	// 字符串常量
	static char strErr[] = "读取模板图像 失败 !";
	return strErr;
}
cpp 复制代码
        private void button1_Click(object sender, EventArgs e)
        {
            // 调用C函数获取字符串
            IntPtr strPtr = strToCSharp();

            string strErr = Marshal.PtrToStringAnsi(strPtr);

            // 在MessageBox中显示
            MessageBox.Show(strErr, "Error");

            // 在ListBox中显示
            listBox1.Items.Add(strErr);
        }

2 通过retun返回 字符串常量

2.1 字符串常量 char* strErr --- 成功

例如:char* strErr= "读取模板图像 失败 !"传给C# ,并且在winform的MessageBox和listbox中显示

将C语言中的字符串常量 char* strErr = "读取模板图像 失败 !"; 传递给C#,并在WinForm的MessageBox和ListBox中显示,你可以创建一个C#的Interop接口来调用C语言的函数,然后将返回的字符串显示在MessageBox和ListBox中。

C语言代码(C.dll):

cpp 复制代码
__declspec(dllexport) const char* strTest()
{
 // 字符串常量
	const char* strErr = "读取模板图像 失败 !";
	return strErr;
}

C#调用:

cpp 复制代码
        [DllImport("LineProApi.dll", EntryPoint = "strTest", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr strTest();


        private void button1_Click(object sender, EventArgs e)
        {        
            // 调用C函数获取字符串
            IntPtr strPtr = strTest();

            string strErr = Marshal.PtrToStringAnsi(strPtr);

            // 在MessageBox中显示
            MessageBox.Show(strErr,"Error");

            // 在ListBox中显示
            listBox1.Items.Add(strErr);
        }

3 将char* strErr字符串 和 int合并传给C# ---成功

将一个整数变量和一个C风格字符串连接在一起,并一起传递给C#,你可以使用sprintf函数将整数转换为字符串,并将两个字符串连接在一起,然后返回这个连接后的C风格字符串指针。

cpp 复制代码
// 定义一个函数,将整数和C风格字符串连接在一起,并返回连接后的字符串指针
const char* strTest()
{
	// 定义一个足够大的字符数组来存储结果字符串
	static char strErr[100];
	int num = 10;

	const char* str1 = "读取第";
	const char* str2 = "幅图像失败 !";

	// 将整数转换为字符串并与C风格字符串连接在一起
	sprintf(strErr,"%s %d %s", str1, num,str2);

	// 返回结果字符串的指针
	return strErr;
}

4 C++ string strErr传给 C# ---失败

C++中的字符串 string strErr = "读取模板图像 失败 !"; 传递给C#,并在WinForm的MessageBox和ListBox中显示,可以通过Interop技术实现。你可以在C++中创建一个动态链接库(DLL),在其中定义一个函数,该函数返回一个指向字符串的指针。然后在C#中引用该DLL,并使用Interop来调用C++函数。

4.1 str.c_str()将字符串转为C语言风格的字符串---失败

cpp 复制代码
extern "C" __declspec(dllexport) const char* strTest()
{
	std::string strErr = "读取模板图像 失败 !";
	// 获取C风格字符串
	const char* c_strErr = strErr .c_str();
	return c_strErr ;
}

4.2 问题分析及解决---成功

strErr是一个本地变量,当 strTest 函数返回时,它将被销毁,导致指向其内部缓冲区的指针 c_strErr 无效化。因此,在C#中接收到的是无效的内存地址,解析为乱码。

为了解决这个问题,你可以通过动态分配内存,确保返回的C风格字符串在C++函数返回后依然有效。 以下是修改后的C++代码:

cpp 复制代码
extern "C" __declspec(dllexport) const char* strTest()
{
    // 构造C++字符串
    std::string strErr = "读取模板图像失败!";
    
    // 动态分配内存给C风格字符串
    char* c_strErr = (char*)malloc((strErr .length() + 1) * sizeof(char));
    
    // 将C++字符串复制到C风格字符串中
    std::strcpy(c_strErr, strErr .c_str());
    
    // 返回C风格字符串
    return c_strErr;
}

我们使用 malloc 函数来动态分配足够的内存给 C 风格字符串,确保在函数返回后依然有效。然后使用 strcpy 将 C++ 字符串复制到这个内存块中。在使用完 C 风格字符串后,记得释放它所占用的内存:

cpp 复制代码
extern "C" __declspec(dllexport) void freeStr(const char* str)
{
	delete[] str;
}

C#调用

cpp 复制代码
        [DllImport("LineProApi.dll", EntryPoint = "strTest", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr strTest();

        [DllImport("LineProApi.dll", EntryPoint = "freeStr", CallingConvention = CallingConvention.Cdecl)]
        public static extern void freeStr(IntPtr str);



        private void button1_Click(object sender, EventArgs e)
        {
            //// 调用C函数获取字符串
            IntPtr strPtr = strTest();
            string strErr = Marshal.PtrToStringAnsi(strPtr);

            // 在MessageBox中显示
            MessageBox.Show(strErr,"Error");

            // 在ListBox中显示
            listBox1.Items.Add(strErr);

            // 释放C风格字符串的内存
            freeStr(strPtr);
        }

5 通过参数传递 字符串

5.1 字符串常量 char* strErr参数传递----失败

cpp 复制代码
void strTest2(const char* strErr)
{
	strErr = "读取模板图像 失败 !";
}
cpp 复制代码
     [DllImport("LineProApi.dll", EntryPoint = "strTest2", CallingConvention = CallingConvention.Cdecl)]
     public static extern void strTest2(out string strErr);
 // public static extern void strTest2([MarshalAs(UnmanagedType.LPStr)] string strErr);

 private void button1_Click(object sender, EventArgs e)
    {
        //// 调用C函数获取字符串
        string strErr = "";
        strTest2(strErr);
        //strTest2(out strErr);

        // 在MessageBox中显示
        MessageBox.Show(strErr,"Error");
        // 在ListBox中显示
        listBox1.Items.Add(strErr);
    }

接收到的字符串为空

5.2 问题分析及解决---成功

strErr 是一个指向 const char 的指针,它只是一个参数,并不是一个可以修改的变量。 在 strTest2 函数内部,你试图将 strErr 指向另一个字符串的地址,但这只会在函数作用域内更改指针的值,并不会影响到调用函数的地方。

如果你想要修改 strErr 指针指向的字符串内容,你需要传递一个指向指针的指针,然后在函数内部修改指针的值。

cpp 复制代码
extern "C" __declspec(dllexport) void strTest2(const char** strErr)
{
    *strErr = "读取模板图像 失败 !";
}

在这个修改后的代码中,strTest2 函数接受一个指向 const char 指针的指针,然后在函数内部修改了指针指向的字符串地址。

在C#中,你可以将一个字符串的引用传递给 strTest2 函数,并通过 out 关键字来传递指针的指针。以下是修改后的C#代码示例:

cpp 复制代码
        // 声明用于调用C++ DLL中的strTest2函数
        [DllImport("YourCppLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void strTest2(out IntPtr strErr);

        private void button1_Click(object sender, EventArgs e)
        {
            // 调用C函数获取字符串
            IntPtr strPtr;
            strTest2(out strPtr);

            // 在C#中使用字符串
            string strErr = Marshal.PtrToStringAnsi(strPtr);

            // 在MessageBox中显示
            MessageBox.Show(strErr, "Error");

            // 在ListBox中显示
            listBox1.Items.Add(strErr);
        }
相关推荐
BestOrNothing_201527 分钟前
【C++基础】Day 4:关键字之 new、malloc、constexpr、const、extern及static
c++·八股文·static·extern·new与malloc·constexpr与const
无敌最俊朗@36 分钟前
如何把一个压缩的视频文件,解压成一张张原始图片-decode_video.c
c++
fpcc36 分钟前
C++编程实践——手动实现std::visit
c++
重启的码农38 分钟前
enet源码解析(4)多通道机制 (Channels)
c++·网络协议
重启的码农44 分钟前
enet源码解析(3)数据包 (ENetPacket)
c++·网络协议
qq_353199251 小时前
鼠标滑动或横拉用户控件无闪缩
c#
wefg12 小时前
【C++】智能指针
开发语言·c++·算法
MSTcheng.2 小时前
【C++模板进阶】C++ 模板进阶的拦路虎:模板特化和分离编译,该如何逐个突破?
开发语言·c++·模板
Demon--hx2 小时前
[c++]string的三种遍历方式
开发语言·c++·算法
valan liya3 小时前
C++list
开发语言·数据结构·c++·list