这是在C++头文件里头的声明extern "C" __declspec(dllexport) char* __stdcall getb(char * str);这是函数的定义char * __stdcall getb(char * str)
{
string strb(str);
char * xstr = (char *)strb.c_str();
return xstr;
}这是C#里头函数导入的代码    public class Utility
    {
        [DllImport("bb.dll", EntryPoint = "_getb@4", CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr getb(IntPtr inp);
    }这里是C#里调用函数的代码    string str = "AAAA";
    IntPtr trb = Utility.getb(Marshal.StringToBSTR(str));
    string b = Marshal.PtrToStringBSTR(trb);觉得好奇怪,怎么调用函数的时候会提示异常:
未处理AccessViolationException
尝试读取或写入受保护的内存。这通常指示其他内存已损坏。
而且如果把函数定义改成直接返回参数的指针,就没有问题:char * __stdcall getb(char * str)
{
return str;
}感觉应该是string里头出问题了吧~但是不知道错哪了,怎么修改,而且开始的想法不是只把char*放到string里头再得到char*,中间还有对那个string进行操作的。
请高手们帮忙找找错误~谢谢了~~

解决方案 »

  1.   

    char * xstr = (char *)strb.c_str();
    改成:strcpy(xstr,strb.c_str());试试。
      

  2.   

    __declspec的申明导出方式
    CallingConvention.StdCall调用
    不对的,应该对应起来。
      

  3.   


    char * __stdcall getb(char * str)
    {
        string strb(str);
        char * xstr = (char *)strb.c_str();
        return xstr;
    }
    这样写有问题。
    string strb(str);
    strb创建在栈中,return xstr导致了strb的析构,strb中创建的字符串也随之丢失。
    xstr指向strb创建的字符串,后果就是xstr指向已经释放的内存。
      

  4.   

    简单的,仅作传入参数用string,输出参数用StringBuilder。
    [DllImport("bb.dll", EntryPoint = "_getb@4", CallingConvention = CallingConvention.StdCall)]
    public static extern string getb(string str);另LZ的C++写法不对,如可以返回std::string类型,不能返回指向函数内部临时变量的指针,这是危险的。
      

  5.   

    返回std::string别说C#,就连C++自己都不能正常调用lz可以
    extern "C" __declspec(dllexport) int __stdcall getb(char * str,char* out);
    这样定义,使用out来返回字符串。
      

  6.   

    3楼正解。
    是你的C++DLL有问题,问题不出在平台调用这里。
    这个DLL本身设计的不够严密,才出现的错误。
      

  7.   

    谢谢各位高手提醒,返回临时变量的指针是错的
    继续请教各位大大
    用输出参数返回临时变量的指针是可以的吗?
    需不需要new?
    但是new了以后在什么地方释放呢?还要另外导出一个释放空间的方法让C#调用吗?
      

  8.   

    麻烦你写一个程序试试好么?
    要不我也一个dll你调用一下?
      

  9.   

    任何时候都不要返回临时变量的指针.
    extern "C" __declspec(dllexport) char* __stdcall getb(char * str);
    假设这里str是作输出参数用,那么定义成:
    [DllImport("bb.dll", EntryPoint = "_getb@4", CallingConvention = CallingConvention.StdCall)] 
    public static extern string getb(StringBuilder sb); StringBuilder sb = new StringBuilder(256);//由C#申请空间。
    getb(sb);//C++里注意内存不要越界。
      

  10.   

    再再次请教大大:
    C++的函数改成了如下:
    int __stdcall getb(char* str,char* out)
    {
    string strb(str);
    int len = strb.length();
    out = new char[len + 1];
    strcpy(out,strb.c_str());
    return 1;
    }
    extern "C" __declspec(dllexport) int __stdcall getb(char * str,char * out);
    导出        [DllImport("bb.dll", EntryPoint = "_getb@8", CallingConvention = CallingConvention.StdCall)]
            public static extern int getb(IntPtr inp,out IntPtr outp);
    调用            string str = "AAAA";
                IntPtr outp;
                Utility.getb(Marshal.StringToBSTR(str),out outp);
                string b = Marshal.PtrToStringBSTR(outp);怎么调用了getb之后,outp是0???
      

  11.   


    还是不行额。。C#里代码修改后如下:        [DllImport("bb.dll", EntryPoint = "_getb@8", CallingConvention = CallingConvention.StdCall)]
            public static extern int getb(string str, StringBuilder strout);            StringBuilder sb = new StringBuilder(100);
                Utility.getb(str, sb);调用完getb以后sb里头没东西。。
      

  12.   

    再sb前头试着加了out 也没用。。sb变成null了。。
    抓狂ing。。
      

  13.   

    int __stdcall getb(char* str,char* out)
    {
        string strb(str);
        int len = strb.length();
        out = new char[len + 1];
        strcpy(out,strb.c_str());
        return 1;
    }
    extern "C" __declspec(dllexport) int __stdcall getb(char * str,char * out);
      

  14.   

    char* __stdcall getbbbb(char* str)
    {
    string strbbb(str);
    int len = strbbb.length();
    char* out = new char[len + 1];
    strcpy(out,strbbb.c_str());
    return out;
    }
    string str = "AAAA";    
            IntPtr ptrout = Utility.getbbb(Marshal.StringToBSTR(str));            string sb = Marshal.PtrToStringBSTR(ptrout);这样的话是“未处理OutOfMemoryException”
    引发类型为“System.OutOfMemoryException”的异常。
    排错提示:
    如果要创建数组,请确保大小正确
    对于内部用途和新的托管对象,确保有足够的内存可供分配。。
    如果这样:        [DllImport("bb.dll", EntryPoint = "_getbbbb@4", CallingConvention = CallingConvention.StdCall)]
            public static extern StringBuilder getbbb(IntPtr inp);            string str = "AAAA";
                StringBuilder sb = new StringBuilder(100);
                sb = Utility.getbbb(Marshal.StringToBSTR(str));StringBulider里头就只有一个A (- -!)
      

  15.   

    谢谢大大们。终于成功了
            [DllImport("bb.dll", EntryPoint = "_getb@4", CallingConvention = CallingConvention.StdCall)]
            public static extern StringBuilder getb(string str);
                StringBuilder sb = new StringBuilder(100);
                sb = Utility.getbbb(str);就是还是搞不明白为什么输出参数用不了呢?
    还有Marshell类的那些转换函数到底用在什么场合呢?string就可以自动转换成char *,返回的时候用StringBuilder也可以自己转换
      

  16.   

    谢谢您的指教,在C中如果给C#调用,是不能返回std::string, 这可能是类的封送问题,如果是给C++调用是可以的。
      

  17.   

    Marshal类是在托管和非托管的转换时会用到。
      

  18.   

    哎。。之前还有试过先用托管C++把一个dll里头导出的函数用一个包装类把返回值和参数都转换了,然后生成一个新的dll让C#调用,结果又是莫名其妙的错误,什么找不到什么文件。。也弄了我半天。。大大们不知道有没有遇到过类似的问题?
    哎。。这阵子太痛苦了,写程序不是这样的问题,就是那样的问题,都搞我个半天,都快没激情了。。
      

  19.   


    那是调用方法不正确,改下如下:
    string str = "AAAA";
    IntPtr ptr= Marshal.StringToCoTaskMemAnsi(str);
    int n= getb(ptr);
    string ss = Marshal.PtrToStringAnsi(ptr);
    Marshal.FreeCoTaskMem(ptr);
      

  20.   


    int __stdcall getb(char* str,char* out)
    {
        string strb(str);
        int len = strb.length();
        //out = new char[len + 1];//去掉这一行
        strcpy(out,strb.c_str());
        return 1;
    }C#调用方法
      [DllImport("bb.dll", EntryPoint = "_getb@8", CallingConvention = CallingConvention.StdCall)]
            public static extern int getb(string str, StringBuilder strout);
     StringBuilder sb = new StringBuilder(100);
                Utility.getb(str, sb);
    这种方法不对,虽然你拿到正确的值,但这是偶然.        [DllImport("bb.dll", EntryPoint = "_getb@4", CallingConvention = CallingConvention.StdCall)]
            public static extern StringBuilder getb(string str);
      

  21.   

    十分感谢啊~两位大大的方法试过都可以:)
    感觉Marshal类里头好多函数很像啊~去msdn里头查下区别先~
      

  22.   

    哎~又看晕了。
    什么样的字符串是ANSI的?又有什么样的是BSTR的呢?还有ASCII。。
    还有这个PtrToStringAuto 已重载。 分配托管 String,并从非托管字符串向其复制指定数目的字符。是什么时候用的呢?