文章目录
- 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);
}