【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);
        }
相关推荐
半桔3 小时前
【STL源码剖析】从源码看 list:从迭代器到算法
java·数据结构·c++·算法·stl·list
拾光Ծ3 小时前
【C++】STL之list模拟实现:关于链表容器的双向迭代器你知道多少?
开发语言·数据结构·c++·list·visual studio
青草地溪水旁3 小时前
VSCode C/C++调试配置文件 `launch.json` 全字段深度解析
c语言·c++·vscode
bkspiderx3 小时前
C++设计模式之行为型模式:命令模式(Command)
c++·设计模式·命令模式
我是唐青枫4 小时前
深入掌握 FluentMigrator:C#.NET 数据库迁移框架详解
数据库·c#·.net
暴力求解4 小时前
数据结构---栈和队列详解(上)
开发语言·数据结构·c++
tiankongdeyige4 小时前
Unity学习之C#的反射机制
学习·unity·c#
小苏兮5 小时前
【C++】list的使用与模拟实现
开发语言·c++·list
绿荫阿广5 小时前
用纯.NET开发并制作一个智能桌面机器人(六):使用.NET开发一个跨平台功能完善的小智AI客户端
c#·.net·asp.net core·maui·winui