C#调用C++的DLL乱码, 函数原型是:char *fuc(char *a,char *b)

hbzykj 2013-04-30 01:19:24
在C语言的标准库函数中,有strcat()函数,它的作用是连接两个字符串,原型如下:

char *strcat(char *strDestination, const char *strSource)

作用是:在 Dest 的后面接上 Src 指向的字符串,把两个字符串连接起来。

我自己实现了 my_strcat(char *dst, char *src)函数,代码如下:
extern "C"
{
__declspec(dllexport) char *my_strcat(char *dst, char *src)
{
char *old_pos = dst;
while (*dst)
{
dst++;
}
while (*dst++ = *src++)
{
;
}
return old_pos;
}
}

上面的函数经过控制台测试,是正常的,然后使用VS2010编译得到 *.dll 动态连接库文件。
在C#中的代码如下:

//导入刚刚生成的 DLL ,函数入口是 my_strcat ,调用方式是 cdecl ,字符集是 unicode

[DllImport("*.dll", EntryPoint = "my_strcat", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern string my_strcat(string a, string b);

//在点击事件中把文本框1中的字符串,文本框2中的字符串连接起来,然后把连接的字符串放在文本框3中。
textBox3.Text = my_strcat(textBox1.Text, textBox2.Text);

但是点击按钮后文本框3中的内容是乱码,请教,如何解决C#调用DLL里,针对 C/C++ 中的char * 函数(char *,char *)形参的调用?并且能正常返回一个字符串?

...全文
1291 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
zengwenjian 2014-03-18
  • 打赏
  • 举报
回复
十分感谢楼主,和楼上的各位大神。 我也是想把自己以前写的C++的代码封装为DLL,用C#调用也是遇到字串乱码的问题,搞了1天快疯了才在楼主这找到答案,真的很感激。
onekey 2013-06-27
  • 打赏
  • 举报
回复
http://www.cnblogs.com/Purple_Xiapei/archive/2012/06/28/2568597.html
hbzykj 2013-05-01
  • 打赏
  • 举报
回复
dest 的空间大小需要调用方来保证,像strcpy / strncpy 也是一样的,strcpy 会强行把字符串末尾加个尾零符,这样会改变字符串的,所以都建议用 strncpy, 其实可以使用malloc动态分配内存,这都是后话了。

C的代码在C#里面复用,我纠结了很久,也没有搞明白,为什么是乱码?到底是哪里出了问题。我本来是想做一个计算器(在控制台中测试已实现),输入表达式字符串,然后根据括号和运算符优先级,计算出结果,想象中用C#做界面,用C在后台去处理字符串和算法。

C中没有string类,处理字符串都是指针或者是数组,如何把字符串在C#和C之间传递成了重中之重。如果把表达式的字符串全部用C#的函数处理后计算,就失去了我本意了。虽然不鼓励这样做,但是我只是做个试验,这样做能不能成功?所以就写了这个strcat函数来测试,能不能返回指针,并且形参也是指针。

如果能接收一个char指针,处理完后返回一个char指针,那么做一个表达式计算器就可能。
hbzykj 2013-05-01
  • 打赏
  • 举报
回复
引用 13 楼 zanfeng 的回复:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        //导入刚刚生成的 DLL ,函数入口是 my_strcat ,调用方式是 cdecl ,字符集是 unicode

        [DllImport("test.dll", EntryPoint = "my_strcat", CharSet = CharSet.Ansi)]
        public static extern string my_strcat(string a, string b);


        private void button1_Click(object sender, EventArgs e)
        {
            //在点击事件中把文本框1中的字符串,文本框2中的字符串连接起来,然后把连接的字符串放在文本框3中。
            textBox3.Text = my_strcat(textBox1.Text, textBox2.Text);
        }
    }
}


extern "C"
{
    __declspec(dllexport) char *my_strcat(char *dst, char *src)
    {
        char *old_pos = dst;
        while (*dst)
        {
            dst++;
        }
        while (*dst++ = *src++)
        {
            ;
        }
        return old_pos;
    }
}

vs2008调用成功。
呵呵,感谢你的回复,我在论坛其它的贴子中看到你回复乱码的问题。 我分别用 VS2010 VS2008 VS2012 三种版本都测试过你的代码 均没有通过,是否哪个地方需要设置?比如工程属性? 我的 QQ 86389981 可以把你的工程文件传给我吗?
hbzykj 2013-05-01
  • 打赏
  • 举报
回复
引用 16 楼 zanfeng 的回复:
vc项目右键设置多字节的。



感谢楼上帮了大忙,真心谢谢你。现献图,以供同样问题的新手参考。
在制作DLL的C++项目中按上图设置,(如果用C的话需要修改预编译头和编译为两项),然后在C#调用的时候做以下映射和设置:
[DllImport("Cal_Dll.dll", EntryPoint = "my_strcat", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern string my_strcat(string a, string b);

textBox3.Text = my_strcat(textBox1.Text, textBox2.Text);


非常感谢!结贴。

足球中国 2013-05-01
  • 打赏
  • 举报
回复
vc项目右键设置多字节的。
足球中国 2013-04-30
  • 打赏
  • 举报
回复

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        //导入刚刚生成的 DLL ,函数入口是 my_strcat ,调用方式是 cdecl ,字符集是 unicode

        [DllImport("test.dll", EntryPoint = "my_strcat", CharSet = CharSet.Ansi)]
        public static extern string my_strcat(string a, string b);


        private void button1_Click(object sender, EventArgs e)
        {
            //在点击事件中把文本框1中的字符串,文本框2中的字符串连接起来,然后把连接的字符串放在文本框3中。
            textBox3.Text = my_strcat(textBox1.Text, textBox2.Text);
        }
    }
}


extern "C"
{
    __declspec(dllexport) char *my_strcat(char *dst, char *src)
    {
        char *old_pos = dst;
        while (*dst)
        {
            dst++;
        }
        while (*dst++ = *src++)
        {
            ;
        }
        return old_pos;
    }
}

vs2008调用成功。
  • 打赏
  • 举报
回复
这类问题最好不要去鼓励,相反地应该警告程序员“不要随便使用c++的dll”。 实际上,你应该让c++程序员(而不是自己)去提供.net封装的dll,并且经过严格测试其不会产生bug特别是泄露。
yuhaoloen 2013-04-30
  • 打赏
  • 举报
回复
可用c++cli封装一层在给c#调用即可了
gomoku 2013-04-30
  • 打赏
  • 举报
回复
引用 楼主 hbzykj 的回复:
我自己实现了 my_strcat(char *dst, char *src)函数 ...
你的C实现会缓存溢出,严重情况下会程序崩溃。
gomoku 2013-04-30
  • 打赏
  • 举报
回复

// 危险代码!!
int i = 0;
char src[] = "hello";
my_strcat(src, "buffer overflow");
printf("i=%x", i);   //i=776f6c66
比如以上代码,src缓冲区的实际大小只有6(五个字符加上一个零休止)。 把另外一个字符串接在它后面,就可能修改了不是src缓冲区的其他数据。 比如以上试验中,i就被覆盖了(不同编译器可能有不同内存布局,可能有不同结果)。如果覆盖的不只是int i,而是影响了线程堆栈,或者其他重要信息,程序就可能崩溃了,甚至可能把你C盘格式化。 你在C#中用string来传入dst,这种情况下dst一点都没有预留空间,几乎肯定要缓冲溢出。 因此,解决问题的关键就要要保证dst主够大,比如: StringBuilder dst = new StringBuilder(1024); //预留1024个字符 dst.Append("hello"); my_strcat(des, " world"); 但是,问题是要预留多大的空间?1024会不会浪费?1024会不会太小? 更好的解决方法,就是明确指示缓冲区的大小。比如,换成如下导出:

void my_str(char* dst, const char* src, int dst_length)
{
  int offset = strnlen(dst, dst_length);
  for(int i = offset; i < dst_length && *src; i++)
  {
    dst[i] = *src++;
  }
}
其中src用const char*修饰,const表示不会被改变,因此可以安全的用C# string传入。 其中dst则可以被改变,它的大小用dst_length来限制。 这也是为什么strcat被标记被不安全代码,而strncat被要求使用的原因。
谁学逆向工程 2013-04-30
  • 打赏
  • 举报
回复
楼主的C版本代码中的 my_strcat 函数有个 bug,你自己发现了吗?那就是缓冲区溢出问题。在你的代码中没有判断des参数的缓冲区是否够用就进行了连接。! 至于C#调用的代码,我还没学那么深,暂时不会。。、
hbzykj 2013-04-30
  • 打赏
  • 举报
回复
引用 5 楼 gomoku 的回复:
[quote=引用 楼主 hbzykj 的回复:] 我自己实现了 my_strcat(char *dst, char *src)函数 ...
你的C实现会缓存溢出,严重情况下会程序崩溃。[/quote] 如果可能,可不可以告诉我在哪个地方应该注意?我是新手。。。 我是第一次发贴,我觉得尽可能在贴子说出自己的想法和遇到的问题。 这样才会有高手看到,并指出自己的不对。
hbzykj 2013-04-30
  • 打赏
  • 举报
回复
引用 6 楼 sp1234 的回复:
这类问题最好不要去鼓励,相反地应该警告程序员“不要随便使用c++的dll”。 实际上,你应该让c++程序员(而不是自己)去提供.net封装的dll,并且经过严格测试其不会产生bug特别是泄露。
嗯,内存泄露的问题确实要注意,不过在写代码的时候注意free就可以使用了吧?我主要是想着把这个C的代码复用,在C#中也可以使用该项功能。如果可能,可不可以帮我看看我的代码哪里出了问题呢?谢谢你。
hbzykj 2013-04-30
  • 打赏
  • 举报
回复
引用 3 楼 stonespace 的回复:
应该是CharSet = CharSet.Unicode这个出了问题,一般来说c/c++缺省的字符集是CharSet.ASCII,你用Unicode传递自然会有乱码
我按你的办法使用了其它字符集,调试的时候VS2010提示:尝试读取或写入受保护的内存。这通常指示其他内存已损坏。 请问还有其它的办法吗?谢谢了,我主要是学纯C开发,C#是我本来就会一些的,而且在未来的工作中,会很少用C#,大多数都是用C的多,我想使用DLL只是想把C的代码复用,要不然的话要用C#重新写一遍。 乱码的问题纠结了我好久,好想找个人帮我调试一下C#的工程文件,我的QQ:86389981 盼高手能指点迷津。
stonespace 2013-04-30
  • 打赏
  • 举报
回复
文件流是c库函数才能使用的东西,c#是没法用的,除非你把c库函数封装成dll,但是没人会这么做, 不要想在c#中操作库函数的流,
引用 2 楼 hbzykj 的回复:
顺便再问一下: 在C#中,如果调用 FILE *fopen(“路径/文件名”, “模式”)这个 fopen 库函数?它返回的是一个文件流指针,在C#中如何操作该指针?若是有高手贴出代码,就太感谢了。
stonespace 2013-04-30
  • 打赏
  • 举报
回复
应该是CharSet = CharSet.Unicode这个出了问题,一般来说c/c++缺省的字符集是CharSet.ASCII,你用Unicode传递自然会有乱码,
hbzykj 2013-04-30
  • 打赏
  • 举报
回复
顺便再问一下: 在C#中,如果调用 FILE *fopen(“路径/文件名”, “模式”)这个 fopen 库函数?它返回的是一个文件流指针,在C#中如何操作该指针?若是有高手贴出代码,就太感谢了。
hbzykj 2013-04-30
  • 打赏
  • 举报
回复
后来,我又在论坛上找到第二个版本如下:

[DllImport("*.dll", EntryPoint = "my_strcat", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern IntPtr my_strcat(string a, string b);
		
IntPtr pStr = my_strcat(textBox1.Text, textBox2.Text);
textBox3.Text = Marshal.PtrToStringAnsi(pStr); 
结果不是乱码了,但是只是把两个字符串的首字母连接起来了,盼好心人能指点一二,给出代码以供参考,并能指出我错在哪里。谢谢了。 参考资料如下: http://blog.csdn.net/xinyaping/article/details/7288164 在此特别感谢金辉软件技术公司的唐先生对我的帮助,谢谢所有帮助过我的人。

110,533

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧