求助:VC中用结构体从DLL返回数据问题,貌似简单,极其郁闷中

bailee 2010-05-22 02:02:38
1、exe程序调用部分:
STRUCT_A recvData;
getValue(recvData);

2、DLL函数部分:
getValue(STRUCT_A & aStructFromExe)
{
aStructFromExe.iNo = 100; //此句没问题,可以赋值
aStructFromExe.strName = "test"; //此句就报错,大意是写冲突
}

3、结构体定义部分:
typedef struct _STRUCT_A_
{
int iNo;
string m_strVsersion;
}STRUCT_A, *PSTRUCT_A;

说明:程序DLL调用、运行都正常,但结构体传到DLL中后,结构体本身的指针没错,可结构体中的字符串、Map等就有问题了;

郁闷好两天了,不要告诉我用char []来代替string,因为这样可以,但不是我想要的。

编译器是VC2008,请各位帮帮忙!!! 先100分送上
...全文
579 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
sunlin7 2010-05-23
  • 打赏
  • 举报
回复
VC6.0调试成功!

把dll和exe都改成“Debug Multithreaded DLL",运行良好。

因此可以佐证是堆管理不统一的问题引起的。(关于这个,在Programming Windows via C++里面的内存管理和dll编写里面有介绍。)

还有,如果仔细研究过pe加载过程的人也知道,crt会在初始化的时候接管堆,并根据pe的配置初始化一个堆。

当我们指定pe入口,撇开crt的时候,就要小心翼翼的自己动手管理内存。
sunlin7 2010-05-23
  • 打赏
  • 举报
回复
楼主在线嘛!呵呵
楼主你把dll以MFC扩展DLL方式重新生成并将exe换成共享链接crt看看。
bailee 2010-05-23
  • 打赏
  • 举报
回复
To BloodFighter:
谢谢,我知道char[]没有问题 :)

不这么用的原因有二:
1、传回来的数据长度不固定,不想浪费内存;
2、需要传回来的数据需要用map、list等类型,如果只是string,牺牲第1条也不是太大的问题。
bailee 2010-05-23
  • 打赏
  • 举报
回复
To yincheng01:
我跟踪调试时,在EXE、DLL中,结构体的地址都是一样的,说明结构体的地址已经传到了DLL中,用到的内存也应该是EXE中的内存(我个人这么认为的,没深究过)。

但在EXE中,strName为"";(正确)
而在DLL中,strName就变为错误的地址了(错误的根源,就是strName的地址错了,你再怎么写都不行)

所以我就有点想不明白了,不信大家也试试,相信有人用过
BloodFighter 2010-05-23
  • 打赏
  • 举报
回复
接口的地方,尽量用基本类型
BloodFighter 2010-05-23
  • 打赏
  • 举报
回复
在结构体中,少用类对象,多用固定结构,特别是你这种跨模块的调用,还是顶一个TCHAR[]吧,保证没问题
bailee 2010-05-23
  • 打赏
  • 举报
回复
这么晚了CSDN上还有这么多兄弟在,看到IT技术行业真是命苦呀,想想做市场的现在可能在哪个酒吧喝酒陪客户呢(别样的辛苦) :)


To HawkOfWinter: 写在DLL中是不一样的 :)

To lisunlin0:有思想,赞一个!

但我想问题肯定不在这里,因为我看到过别人也这么用过,可我就搞不定 :)
wltg2001 2010-05-23
  • 打赏
  • 举报
回复
vs2005,我直接用上面的例子,并没报错。相反,运行正确。

难道写在dll里,不一样?
===========
string的大小是动态的,你的做法应该是在EXE中定义STRUCT_A,而在DLL中引用STRUCT_A,而在DLL中引用时,string的大小是会动态改变的,而DLL与EXE的堆并不一定相同,在动态改变大小就会出现错误。
如果仅写在EXE中,当然没有问题
zhouzhipen 2010-05-23
  • 打赏
  • 举报
回复
好像关于这个问题,在MSDN中也明确的说明。
bailee 2010-05-23
  • 打赏
  • 举报
回复
To ALL:
已经搞定了(但真正原因我还不明),谢谢各位!!!

仔细对比了一下EXE中各字段的地址与DLL中的各字段的地址,虽然在EXE与DLL中结构体的地址是一样的,但结构体中字段的地址却不一致,可能是编译选项的问题,一顿乱设,OK了!

结帖,在此除了谢谢各位走过、路过、留过言的朋友,

特别谢谢lisunlin0、zwfgdlc、yincheng01

zhouzhipen 2010-05-23
  • 打赏
  • 举报
回复
[Quote=引用楼主 bailee 的回复:]
1、exe程序调用部分:
STRUCT_A recvData;
getValue(recvData);

2、DLL函数部分:
getValue(STRUCT_A & aStructFromExe)
{
aStructFromExe.iNo = 100; //此句没问题,可以赋值
aStructFromExe.strName = "test"; //此句就……
[/Quote]


这个十有八九会出错,因为你犯了个一大禁忌!
你用string对像,在=操作时,会产生内存分配操作,但string对像会把内存指针带出DLL,并在外部对其进行释放。

而在WIN平台上,new会被转换成相应的WINAPI的操作,同时对应的delete也会被转换。所以如果是同时编译的程序,是没有什么问题的。但是DLL与主调程序不是一同编译的,所以无法确保转换的正确性。而标准库中一般是直接使用new 和delete 来管理内存的。
所以你的程序十有八九要出错的。

只要你遵循一个原则:
一个程序中所申请的内存只在该程序内释放。就不会出错了。
zwfgdlc 2010-05-23
  • 打赏
  • 举报
回复
我用VC6试了下,似乎也正常。
睡觉,明天再研究。
尹成 2010-05-22
  • 打赏
  • 举报
回复
含有指针的成员结构体内存释放:
在结构体中含有指针的成员 在给结构体分配内存时,需要通过计算这些成员变量总共占多少内存 给与分配,如果少了会出现释放出错 ,并且在赋值是需与成员变量顺序一一对应。
所以好好看看你关于这方面的内存释放问题。
中才德创 2010-05-22
  • 打赏
  • 举报
回复
#include <string>
using namespace std;

//1、exe程序调用部分:
//STRUCT_A recvData;
//getValue(recvData);

//3、结构体定义部分:
typedef struct _STRUCT_A_
{
int iNo;
string m_strVsersion;
} STRUCT_A, *PSTRUCT_A;

//2、DLL函数部分:
void getValue(STRUCT_A & aStructFromExe)
{
aStructFromExe.iNo = 100; //此句没问题,可以赋值
aStructFromExe.m_strVsersion = "test"; //此句就报错,大意是写冲突
}


int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);

// TODO: 在此放置代码。
STRUCT_A recvData;
getValue(recvData);


vs2005,我直接用上面的例子,并没报错。相反,运行正确。

难道写在dll里,不一样?
sunlin7 2010-05-22
  • 打赏
  • 举报
回复
dll与exe使用类实例通信的话要用MFC扩展dll机制。
从windows角度来看,不同的模块的堆是不一定相同的,也就是说exe与dll使用的堆并不是同一个,
而string和map这样的容器都会动态管理内存,假设string在exe里面的长度为1byte,现在到dll里面要扩展成5byte,那么string会先在dll里面分配空间,然后试图释放1byte的内存空间,而这个内存是在exe堆里面的,在dll里面的释放动作却是释放dll堆里面的内存,就导致了访问违列。

解决办法就是使用原始内存(静态的char数组)或者统一分配/释放(用GetProcessHeap后分配的内存并对应释放,或者用VirtualAlloc产生的内存等等,包括MFC的扩展DLL机制。)
bailee 2010-05-22
  • 打赏
  • 举报
回复
感谢大家,发帖时结构体中字义字段m_strVsersion应该是strName,各位兄弟都没有深纠,说明我的这点小错误大家还是能看明白及理解的;

其实用strcpy/format/append都是一样,还是会有问题。
因为我跟踪调试时,结构体中的string/CString/map等类型的地址都已经出错了(要么是地址错误,要么是内容很怪,我个人认为是地址已经不对了,或是指针指向的空间有误了),这就是我想不明白的地方!!!

解决不了也没问题,自己再多想想办法,

本着问题要搞清楚的原则,望各位兄弟继续,周一下班前结帖 :)
zhou1xp 2010-05-22
  • 打赏
  • 举报
回复
aStructFromExe.strName.format(test)看看
zwfgdlc 2010-05-22
  • 打赏
  • 举报
回复

又当成char了,
试下

aStructFromExe.strName = "test";


aStructFromExe.strName.append("test");
zhou1xp 2010-05-22
  • 打赏
  • 举报
回复
楼主看看VC2008是怎么重载string的=号的
zwfgdlc 2010-05-22
  • 打赏
  • 举报
回复
aStructFromExe.strName = "test"; 
换成用strcpy拷贝行不行
加载更多回复(2)
一、 课程设计目的 学会用C++语言和数据结构知识实现表达式的解析与计算;学会使用动态链接库技术进行编程;学会编辑、编译、运行MFC应用程序的基本过程.学会MFC可视化编程技术。 二、 课程设计内容与实现的功能 1.C++语言的顺序结构,分支结构,循环结构,函数,结构体,指针,MFC可视化编程技术。 2.数据结构的二叉树数据组织、存贮、后序遍历及其操作。 3. 使用动态链接库进行函数模块的设计,实现计算功能。 4.编程使用了动态连接库技术; 能实现表达式的输入,解析与计算 三、 系统分析与设计 1、系统分析 应用程序使用DLL可以采用两种方式:一种是隐式链接,另一种是显式链接。在使用DLL之前首先要知道DLL函数的结构信息。Visual C++6.0在VC in目录下提供了一个名为Dumpbin.exe的小程序,用它可以查看DLL文件的函数结构。另外,Windows系统将遵循下面的搜索顺序来定位DLL: 1.包含EXE文件的目录,2.进程的当前工作目录, 3.Windows系统目录, 4.Windows目录,5.列在Path环境变量的一系列目录。 1.隐式链接 隐式链接就是在程序开始执行时就将DLL文件加载到应用程序当。实现隐式链接很容易,只要将导入函数关键字_declspec(dllimport)函数名等写到应用程序相应的头文件就可以了 2.显式链接 显式链接是应用程序在执行过程随时可以加载DLL文件,也可以随时卸载DLL文件,这是隐式链接所无法作到的,所以显式链接具有更好的灵活性,对于解释性语言更为合适。不过实现显式链接要麻烦一些。在应用程序 LoadLibrary或MFC提供的AfxLoadLibrary显式的将自己所做的动态链接库调进来,动态链接库的文件名即是上述两个函数的参数,此后再用GetProcAddress()获取想要引入的函数。自此,你就可以象使用如同在应用程序自定义的函数一样来调用此引入函数了。在应用程序退出之前,应该用FreeLibrary或MFC提供的AfxFreeLibrary释放动态链接库。 2、系统设计: 按照系统设计要求,用Visual C++设计和开发一个MFC应用程序---高级计算器。计算器的界面设计、计算功能实现、算式计算和动态链接库的使用等。提交由需求分析:系统设计说明、系统技术文档、系统使用说明书和源程序代码为附录构成的实验报告。 2.1、模块设计: 1. 计算器界面 计算器界面包括主题“高级计算器”和背景图片以及电子日历。在计算区上有显示文本框和数字按钮以及运算符按钮。在计算区上部分有运算模式选择模块和小提示语句。 2. 模块功能简介 系统分为多个模块,分别为计算模块、错误分析模块、界面显示模块和模式选择模块。其计算模块用来进行各种基本的加、减、乘、除的运算并且显示运算结果;错误分析模块用来进行错误处理;界面显示模块用来美化计算器界面;模式选择模块用来进行运算模式的选择和切换,以实现不同的运算要求

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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