VC多模块程序设计与发布需要注意的细节
2010-05-23 18:09
288 查看
多模块(dll)能让比较大的功能分散到小的接口,这点让软件开发可以让不同的开发人员负责的好处让许多程序都采用了多模块开大方法。
编译为dll的时候,可以选择动态链接到MFC (这里用MFC代替MFC和ATL)还是静态链接到MFC或者选择不使用MFC,采用Windows标准库。他们有什么不同呢?
一、如果采用windows标准库,则不能使用MFC版本的CString,即头文件<afxstr.h>,当然还是可以用ATL版本的CString,即头文件<atlstr.h>,先不讨论这两者的暧昧关系,假设上文提到的CString是MFC版本的,使用windows标准库的时候,与MFC相关的函数不能使用了,头文件<afx.h>不能被包含,否则编译通不过。编译出来的dll文件默认只与windows标准库有关系,如果只有内存处理函数,则该dll只依赖于"kernel32.dll",而不依赖于MSVCRT*.dll(用MSVCRT*.dll代替MSVCRT*.dll和MFC*.dll)。这样编译出来的所有dll,发布的时候,只需要保证目标计算机的windows版本和开发版本兼容,那么发布之后,肯定能运行起来。
二、如果采用动态链接到MFC
1.如果是用VC6.0开发的程序,保证目标机器上MFC42*.dll,这样编译出来的程序定能正常只用,估计这也是为什么VC6.0还这么多程序员或者公司在用的原因。
2.如果是用高于VC6.0的版本开发,不同模块的开发环境最好是一样的,因为在windows xp之后,对每一个程序引入了一个叫清单的资源,exe文件的清单ID为1,dll文件的清单ID为2,清单里面描述了此可执行文件运行时候所需要的MFC版本,例如下面的一个清单文件,用exe资源查看器打开可执行文件或者用vc2008打开exe文件可以看到。
通过xml文件描述了依赖项,assemblyIdentity节点的属性组合成的文件夹“x86_Microsoft.VC90.DebugMFC_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_2a62a75b”在系统的“C:/WINDOWS/WinSxS”目录下面,此文件夹的其它字符串“1fc8b3b9a1e18e3b”在“VC/atlmfc/include/MFCassem.h”里面有定义,另外一个字符串x-ww_2a62a75b还不清楚从哪里来,做什么,不过前面的那么多字符串已经能确定唯一的文件夹了,在这个文件夹下面有MFC*.dll,程序运行之后会加载该目录下的MFC*.dll,又因为一个程序加载的dll名字肯定不能相同,不能加载不同目录下的同一个文件名的dll,因此, 如果不同环境开发的的dll可能因为清单描述的不一样导致加载失败或者运行错误,另外有一点要注意的是,清单文件在可执行文件内部和在可执行文件同目录下而且和可执行文件同名的清单文件(.manifest)具有同样的功能,估计这也是为什么VC2005提供了新版本的depends工具的原因。
三、如果采用静态链接到MFC
静态链接MFC的时候需要注意的问题要多一些,选择静态链接的一个主要原因也应该是发布方便,静态链接主要是内存的分配与释放引起的问题
因为C++里面的new或者malloc最后都会调用HeapAlloc,HeapAlloc的第一个参数是堆句柄,每一个dll里面的堆句柄是在加载dll的时候初始化完成的,初始化的时候会查找当前模块的堆句柄,如果没有,则创建,所以每一个dll里面都有一个堆句柄。利用delete删除内存的时候最后都会调用HeapFree,HeapFree的第一个参数还是堆句柄,删除内存的堆句柄应和分配时表示一致,要满足这一点,dll不能提供函数void GetMemory(char** p)之类的函数然后调用者使用完之后再delete p,如果一定要提供此函数,则应该再提供另外一个函数 void Delete(void* p)来删除自己new出来的内存
比较隐晦的内存分配,C++中有一些明显看不出来的内存分配类,例如CStirng, string, vector,下面就例子来说明静态链接MFC的dll导出函数内存分配注意事项。
因为pstr在dll中new,而且没有提供删除函数,所以调用者在调用pstr的时候会出错
当返回值不为空的时候,返回值的CString是在dll里面创建的,当vector析构时候,会删除CString,CString不是在调用者里面new出来的,所以错了
如果vectStr在函数内部增加了元素,当函数返回后,vector析构,在删除新加进去的元素时会报错,如果给参数加上const则是好例子
这里有一个疑问,为什么作为容器的vector这么爱受伤,但是作为字符串容器的CString确没有什么问题呢,例如下面的函数
想一想,CString的参数有一个内存管理类,CString包含这个内存管理变量,这个内存管理变量和模块想关联,所以无论CString传递到了哪里,都会正常的删除,因为删除的时候会调用内存管理变量的删除函数,它一定和分配函数一致,string和CString类似,list,map和vecotr类似。
为什么动态链接MFC不会出现内存问题呢,因为new和delete都在MSVCRT*.dll里面,所以谁来删除都会调用到同一个分配或者释放,无需担心,
四、无论是动态链接MFC还是静态链接MFC需要注意的问题
map不能通过指针(引用)传递掉别的模块,只能传值,因为map里面都包含了tree,而因为tree里面有一个静态变量,不同模块保存了不同的一份副本(和魔兽副本同义),如传值真,这份副本将不会正确的复制,导致访问出错,不知道现在的版本还会不会这样
如果使用了MFC,而且用了可能调用资源的类,例如CDialog,CBitmap,则应该在函数入口处使用AFX_MANAGE_STATE(AfxGetStaticModuleHandle()),来切换资源,至于还有没有在其它情况下也需要使用这个,还不清楚。
依靠回忆所写,恐有不准确
编译为dll的时候,可以选择动态链接到MFC (这里用MFC代替MFC和ATL)还是静态链接到MFC或者选择不使用MFC,采用Windows标准库。他们有什么不同呢?
一、如果采用windows标准库,则不能使用MFC版本的CString,即头文件<afxstr.h>,当然还是可以用ATL版本的CString,即头文件<atlstr.h>,先不讨论这两者的暧昧关系,假设上文提到的CString是MFC版本的,使用windows标准库的时候,与MFC相关的函数不能使用了,头文件<afx.h>不能被包含,否则编译通不过。编译出来的dll文件默认只与windows标准库有关系,如果只有内存处理函数,则该dll只依赖于"kernel32.dll",而不依赖于MSVCRT*.dll(用MSVCRT*.dll代替MSVCRT*.dll和MFC*.dll)。这样编译出来的所有dll,发布的时候,只需要保证目标计算机的windows版本和开发版本兼容,那么发布之后,肯定能运行起来。
二、如果采用动态链接到MFC
1.如果是用VC6.0开发的程序,保证目标机器上MFC42*.dll,这样编译出来的程序定能正常只用,估计这也是为什么VC6.0还这么多程序员或者公司在用的原因。
2.如果是用高于VC6.0的版本开发,不同模块的开发环境最好是一样的,因为在windows xp之后,对每一个程序引入了一个叫清单的资源,exe文件的清单ID为1,dll文件的清单ID为2,清单里面描述了此可执行文件运行时候所需要的MFC版本,例如下面的一个清单文件,用exe资源查看器打开可执行文件或者用vc2008打开exe文件可以看到。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.VC90.DebugCRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity> </dependentAssembly> </dependency> </assembly>
通过xml文件描述了依赖项,assemblyIdentity节点的属性组合成的文件夹“x86_Microsoft.VC90.DebugMFC_1fc8b3b9a1e18e3b_9.0.21022.8_x-ww_2a62a75b”在系统的“C:/WINDOWS/WinSxS”目录下面,此文件夹的其它字符串“1fc8b3b9a1e18e3b”在“VC/atlmfc/include/MFCassem.h”里面有定义,另外一个字符串x-ww_2a62a75b还不清楚从哪里来,做什么,不过前面的那么多字符串已经能确定唯一的文件夹了,在这个文件夹下面有MFC*.dll,程序运行之后会加载该目录下的MFC*.dll,又因为一个程序加载的dll名字肯定不能相同,不能加载不同目录下的同一个文件名的dll,因此, 如果不同环境开发的的dll可能因为清单描述的不一样导致加载失败或者运行错误,另外有一点要注意的是,清单文件在可执行文件内部和在可执行文件同目录下而且和可执行文件同名的清单文件(.manifest)具有同样的功能,估计这也是为什么VC2005提供了新版本的depends工具的原因。
三、如果采用静态链接到MFC
静态链接MFC的时候需要注意的问题要多一些,选择静态链接的一个主要原因也应该是发布方便,静态链接主要是内存的分配与释放引起的问题
因为C++里面的new或者malloc最后都会调用HeapAlloc,HeapAlloc的第一个参数是堆句柄,每一个dll里面的堆句柄是在加载dll的时候初始化完成的,初始化的时候会查找当前模块的堆句柄,如果没有,则创建,所以每一个dll里面都有一个堆句柄。利用delete删除内存的时候最后都会调用HeapFree,HeapFree的第一个参数还是堆句柄,删除内存的堆句柄应和分配时表示一致,要满足这一点,dll不能提供函数void GetMemory(char** p)之类的函数然后调用者使用完之后再delete p,如果一定要提供此函数,则应该再提供另外一个函数 void Delete(void* p)来删除自己new出来的内存
比较隐晦的内存分配,C++中有一些明显看不出来的内存分配类,例如CStirng, string, vector,下面就例子来说明静态链接MFC的dll导出函数内存分配注意事项。
void GetString(CString** pstr); // 坏例子
因为pstr在dll中new,而且没有提供删除函数,所以调用者在调用pstr的时候会出错
vector<CString> GetStringVector(); // 坏例子
当返回值不为空的时候,返回值的CString是在dll里面创建的,当vector析构时候,会删除CString,CString不是在调用者里面new出来的,所以错了
void fun(vector<CString>& str); // 坏例子
如果vectStr在函数内部增加了元素,当函数返回后,vector析构,在删除新加进去的元素时会报错,如果给参数加上const则是好例子
void fun(const vector<CString>& vectStr); // 好例子
这里有一个疑问,为什么作为容器的vector这么爱受伤,但是作为字符串容器的CString确没有什么问题呢,例如下面的函数
void ModifyCString(CString& str); // 好例子
想一想,CString的参数有一个内存管理类,CString包含这个内存管理变量,这个内存管理变量和模块想关联,所以无论CString传递到了哪里,都会正常的删除,因为删除的时候会调用内存管理变量的删除函数,它一定和分配函数一致,string和CString类似,list,map和vecotr类似。
为什么动态链接MFC不会出现内存问题呢,因为new和delete都在MSVCRT*.dll里面,所以谁来删除都会调用到同一个分配或者释放,无需担心,
四、无论是动态链接MFC还是静态链接MFC需要注意的问题
map不能通过指针(引用)传递掉别的模块,只能传值,因为map里面都包含了tree,而因为tree里面有一个静态变量,不同模块保存了不同的一份副本(和魔兽副本同义),如传值真,这份副本将不会正确的复制,导致访问出错,不知道现在的版本还会不会这样
如果使用了MFC,而且用了可能调用资源的类,例如CDialog,CBitmap,则应该在函数入口处使用AFX_MANAGE_STATE(AfxGetStaticModuleHandle()),来切换资源,至于还有没有在其它情况下也需要使用这个,还不清楚。
依靠回忆所写,恐有不准确
相关文章推荐
- VC多模块程序设计与发布需要注意的细节(zhuanzai)
- 用flash builder创建手机项目以及发布app需要注意的细节
- 关于归并排序 需要注意的细节
- C++中需要注意的细节
- 作为Linux小白,一些需要注意的小细节
- Android开发的一些需要注意的细节
- 问题3:在网上发布信息你认为需要注意什么?不少于50字
- 工作笔记——工作中需要注意的细节的处理方式
- 用MATLAB作图像识别所需要注意的细节!
- 【原创】JMS两种消息模型(点对点,发布订阅)的注意细节
- 【基础知识】c++字符串中需要注意的一些细节
- arguments 向array转化中 需要注意的一个小细节
- idea创建maven web项目需要注意的一些细节
- Android开发的一些需要注意的细节
- 提高你的PHP编程效率需要注意的一些小细节
- MongoDB优化与一些需要注意的细节
- maven项目发布到tomcat以及导包需要注意的几个小问题。
- Java transient关键字使用小结及一些需要注意的细节
- Java 编程需要注意的细节
- 细节决定成败,注意的事情需要做到,而不是听完了当耳边风