stdafx.h的作用
Standard Application Fram Extend
没有函数库,只是定义了一些环境参数,使得编译出来的程序能在32位的操作系统环境下运行。 Windows和MFC的include文件都非常大,即使有一个快速的处理程序,编译程序也要花费相当长的时间来完成工作。由于每个.CPP文件都包含相同的include文件,为每个.CPP文件都重复处理这些文件就显得很傻了。 为避免这种浪费,AppWizard和VisualC++编译程序一起进行工作,如下所示: ◎AppWizard建立了文件stdafx.h,该文件包含了所有当前工程文件需要的MFC include文件。且这一文件可以随被选择的选项而变化。 ◎AppWizard然后就建立stdafx.cpp。这个文件通常都是一样的。 ◎然后AppWizard就建立起工程文件,这样第一个被编译的文件就是stdafx.cpp。 ◎当VisualC++编译stdafx.cpp文件时,它将结果保存在一个名为stdafx.pch的文件里。(扩展名pch表示预编译头文件。) ◎当VisualC++编译随后的每个.cpp文件时,它阅读并使用它刚生成的.pch文件。VisualC++不再分析Windowsinclude文件,除非你又编缉了stdafx.cpp或stdafx.h。 这个技术很精巧,你不这么认为吗?(还要说一句,Microsoft并非是首先采用这种技术的公司,Borland才是。)在这个过程中你必须遵守以下规则: ◎你编写的任何.cpp文件都必须首先包含stdafx.h。 ◎如果你有工程文件里的大多数.cpp文件需要.h文件,顺便将它们加在stdafx.h(后部)上,然后预编译stdafx.cpp。 ◎由于.pch文件具有大量的符号信息,它是你的工程文件里最大的文件。 如果你的磁盘空间有限,你就希望能将这个你从没使用过的工程文件中的.pch文件删除。执行程序时并不需要它们,且随着工程文件的重新建立,它们也自动地重新建立。
VC中Debug和Release出错的问题解决办法
一、内存分配问题
1.变量未初始化。下面的程序在debug中运行的很好。
thing * search(thing * something) BOOL found; for(int i = 0; i < whatever.GetSize(); i++) { if(whatever[i]->field == something->field) { /* found it */ found = TRUE; break; } /* found it */ } if(found) return whatever[i]; else return NULL;而在release中却不行,因为debug中会自动给变量初始化found=FALSE,而在release版中则不会。所以尽可能的给变量、类或结构初始化。2.数据溢出的问题
如:char buffer[10];
int counter;lstrcpy(buffer, "abcdefghik");
在debug版中buffer的NULL覆盖了counter的高位,但是除非counter>16M,什么问题也没有。但是在release版中,counter可能被放在寄存器中,这样NULL就覆盖了buffer下面的空间,可能就是函数的返回地址,这将导致ACCESS ERROR。
3.DEBUG版和RELEASE版的内存分配方式是不同的 。如果你在DEBUG版中申请 ele 为 6*sizeof(DWORD)=24bytes,实际上分配给你的是32bytes(debug版以32bytes为单位分配), 而在release版,分配给你的就是24bytes(release版以8bytes为单位),所以在debug版中如果你写ele[6],可能不会有什么问题,而在release版中,就有ACCESS VIOLATE。
二、ASSERT和VERIFY
1.ASSERT在Release版本中是不会被编译的。
ASSERT宏是这样定义的
#ifdef _DEBUG
#define ASSERT(x) if( (x) == 0) report_assert_failure() #else #define ASSERT(x) #endif实际上复杂一些,但无关紧要。假如你在这些语句中加了程序中必须要有的代码比如ASSERT(pNewObj = new CMyClass);
pNewObj->MyFunction();
这种时候Release版本中的pNewObj不会分配到空间
所以执行到下一个语句的时候程序会报该程序执行了非法操作的错误。这时可以用VERIFY :
#ifdef _DEBUG
#define VERIFY(x) if( (x) == 0) report_assert_failure() #else #define VERIFY(x) (x) #endif这样的话,代码在release版中就可以执行了。三、参数问题:
自定义消息的处理函数,必须定义如下:
afx_msg LRESULT OnMyMessage(WPARAM, LPARAM);
返回值必须是HRESULT型,否则Debug会过,而Release出错
四、内存分配
保证数据创建和清除的统一性:如果一个DLL提供一个能够创建数据的函数,那么这个DLL同时应该提供一个函数销毁这些数据。数据的创建和清除应该在同一个层次上。
五、DLL的灾难
人们将不同版本DLL混合造成的不一致性形象的称为 “动态连接库的地狱“(DLL Hell) ,甚至微软自己也这么说()。
如果你的程序使用你自己的DLL时请注意:
1.不能将debug和release版的DLL混合在一起使用。debug都是debug版,release版都是release版。
解决办法是将debug和release的程序分别放在主程序的debug和release目录下
2.千万不要以为静态连接库会解决问题,那只会使情况更糟糕。
六、RELEASE板中的调试 :
1.将ASSERT() 改为 VERIFY() 。找出定义在"#ifdef _DEBUG"中的代码,如果在RELEASE版本中需要这些代码请将他们移到定义外。查找TRACE(...)中代码,因为这些代码在RELEASE中也不被编译。 请认真检查那些在RELEASE中需要的代码是否并没有被便宜。
2.变量的初始化所带来的不同,在不同的系统,或是在DEBUG/RELEASE版本间都存在这样的差异,所以请对变量进行初始化。
3.是否在编译时已经有了警告?请将警告级别设置为3或4,然后保证在编译时没有警告出现.
七、将Project Settings" 中 "C++/C " 项目下优化选项改为Disbale(Debug)。编译器的优化可能导致许多意想不到的错误,请参考
1.此外对RELEASE版本的软件也可以进行调试,请做如下改动:
在"Project Settings" 中 "C++/C " 项目下设置 "category" 为 "General" 并且将"Debug Info"设置为 "Program Database"。
在"Link"项目下选中"Generate Debug Info"检查框。
"Rebuild All"
如此做法会产生的一些限制:
无法获得在MFC DLL中的变量的值。
必须对该软件所使用的所有DLL工程都进行改动。
另:
MS BUG:MS的一份技术文档中表明,在VC5中对于DLL的"Maximize Speed"优化选项并未被完全支持,因此这将会引起内存错误并导致程序崩溃。
2.debugger)就可以观看所有程序的OutputDebugString的输出。此后,你可以脱离VC来运行你的程序并观看调试信息。
3.有一个叫Gimpel Lint的静态代码检查工具,据说比较好用。 不过要花$的。
DLL中导出函数的两种方式(dllexport与.def文件)(2009-03-06 11:34:58)
DLL中导出函数的声明有两种方式:
一种方式是:在函数声明中加上__declspec(dllexport);
另外一种方式是:采用模块定义(.def)文件声明,(.def)文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。方式一:在函数声明中加上__declspec(dllexport)/// 在动态链接库程序中
/// 声明动态链接库(**.dll)的对外接口函数TestFuctionextern "C" __declspec(dllexport) int TestFuction(int nType,char *strPath,std::vector<string> &vecData){ do anything herereturn 0;}/// 在外部希望调用动态链接库的程序中
/// 加载动态链接库(**.dll)并调用其对外接口TestFuctionvoid func(){ //typedef与函数TestFuction类型相同的函数指针为TESTDLLtypedef int (_cdecl * TESTDLL)(int nType,char *strPath,std::vector<string> &vecData);HINSTANCE hmod;//加载动态链接库**.dllhmod =::LoadLibrary(_TEXT("dll相对路径\\**.dll"));if(NULL == hmod){ TRACE("加载**.dll失败");}//定义一个与函数TestFuction类型相同的函数指针lpprocTESTDLL lpproc;//搜索**.dll中函数名为TestFuction的对外接口lpproc = (TESTDLL)GetProcAddress (hmod,"TestFuction");//如果搜索成功if(NULL != lpproc){ int nType = 0;char* strPath = "Data";std::vector<string> vecData;//通过函数指针lpproc调用**.dll的接口函数TestFuctionint nResult = (*lpproc)(nType,strPath,vecData);}//...//在恰当的时候释放动态链接库**.dllFreeLibrary(hmod);}方式二:采用模块定义(.def)文件声明首先创建 一个DLL程序(DllTestDef)
在*.cpp中int __stdcall Add(int numa, int numb){ return (numa + numb);}int __stdcall Sub(int numa, int numb)
{ return (numa - numb);}然后创建一个.def的文件,在里面加上
;DllTestDef.lib : 导出DLL函数
;作者:----LIBRARY DllTestDefEXPORTSAdd @ 1Sub @ 2最后创建一个测试程序:.cpp文件如下:
#include <iostream>#include <windows.h>using namespace std;
typedef int (__stdcall *FUN)(int, int);
HINSTANCE hInstance;FUN fun;int main()
{ hInstance = LoadLibrary("DLLTestDef.dll");if(!hInstance)cout << "Not Find this Dll" << endl;fun = (FUN)GetProcAddress(hInstance, MAKEINTRESOURCE(1));if (!fun){ cout << "not find this fun" << endl;}cout << fun(1, 2) << endl;FreeLibrary(hInstance);return 0;}说明:
.def文件的规则为:(1)LIBRARY语句说明.def文件相应的DLL;
(2)EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号将发挥其作用);
(3).def 文件中的注释由每个注释行开始处的分号 (;) 指定,且注释不能与语句共享一行。
(4)使用__declspec(dllexport)和使用.def文件是有区别的。
如果你的DLL是提供给VC用户使用的,你只需要把编译DLL时产生的.lib提供给用户,
它可以很轻松地调用你的DLL。但是如果你的DLL是供VB、PB、Delphi用户使用的,那么会产生一个小麻烦。因为VC++编译器对于__declspec(dllexport)声明的函数会进行名称转换,如下面的函数:__declspec(dllexport) int __stdcall Add()会转换为,这样你在VB中必须这样声明:Declare Function Add Lib "DLLTestDef.dll" Alias "" () As Long@后面的数由于参数类型不同而可能不同。这显然不太方便。所以如果要想避免这种转换,就要使用.def文件方式导出函数了。