VC工程项目配置

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

VC⼯程项⽬配置
VC⼯程项⽬配置
⼀、VC包含⽬录、附加依赖项、库⽬录及具体设置
VS项⽬中的包含⽬录、库⽬录、附加包含⽬录、附加库⽬录、附加依赖项均在"项⽬->属性->配置属性"下进⾏配置,具体说明如下:
VC++⽬录:
包含⽬录:寻找#include中的xxxx.h的搜索⽬录
库⽬录:寻找.lib⽂件的搜索⽬录
C/C++:
常规->附加包含⽬录:寻找#include中的xxxx.h的搜索⽬录(每⼀项对应⼀个⽂件夹XXXX,⽂件夹中包含了编译时所需的头⽂件,使⽤时直接
#include即可)
链接器:
常规->附加库⽬录:寻找.lib⽂件的搜索⽬录
输⼊->附加依赖项:lib库(C++的库会把函数、类的声明放在*.h中,实现放在*.cpp或*.cc中。

编译之后,*.cpp,*.cc,*.c会被打包成⼀个.lib⽂件,这样可以保护源代码)
常见问题:
1.包含⽬录和附加包含⽬录(库⽬录和附加库⽬录)的区别:
包含⽬录:修改了系统的include宏的值,是全局的;
附加包含⽬录:⽤于当前项⽬,对其他项⽬没有影响。

(库⽬录和附加库⽬录的区别同上)
2.可知包含⽬录和附加包含⽬录(库⽬录和附加库⽬录)的区别主要在于全局还是当前,那么当需要对某⼯程添加这些⽬录时,通常情况下,都是在附加包含⽬录和附加库⽬录中添加的。

3. 要使⽤⼀个库,除了要include其头⽂件以外(附加包含⽬录),还要在链接过程中把lib加进去(附加库⽬录、附加依赖项)。

4.添加⽅法:
附加包含⽬录---添加⼯程的头⽂件⽬录:
项⽬->属性->配置属性->C/C++->常规->附加包含⽬录:加上头⽂件的存放⽬录;
附加库⽬录---添加⽂件引⽤的lib静态库路径:
项⽬->属性->配置属性->链接器->常规->附加库⽬录:加上lib⽂件的存放⽬录;
附加依赖项---添加⼯程引⽤的lib⽂件名:
项⽬->属性->配置属性->链接器->输⼊->附加依赖项:加上lib⽂件名。

5.当需要向项⽬中添加.dll动态链接库时,直接将需要添加的.dll⽂件拖拽到项⽬⽣成的.exe所在的⽂件夹下即可(项⽬->属性->配置属性->常规->输出⽬录,可以看到.exe⽣成在哪个⽬录下)。

6.在添加上述⼏个⽬录的路径的时候,可以看到$(xxxx),这就是宏了,可以点开右下⾓的宏查看这些分别代表什么字符串。

⼆、lib 和dll 的区别、⽣成以及使⽤详解
⾸先介绍⼀下静态库(静态链接库)、动态库(动态链接库)的概念,⾸先两者都是代码共享的⽅式。

静态库:在链接步骤中,连接器将从库⽂件取得所需的代码,复制到⽣成的可执⾏⽂件中,这种库称为静态库,其特点是可执⾏⽂件中包含了库代码的⼀份完整拷贝;缺点就是被多次使⽤就会有多份冗余拷贝。

即静态库中的指令都全部被直接包含在最终⽣成的EXE ⽂件中了。

在vs中新建⽣成静态库的⼯程,编译⽣成成功后,只产⽣⼀个.lib⽂件
动态库:动态链接库是⼀个包含可由多个程序同时使⽤的代码和数据的库,DLL不是可执⾏⽂件。

动态链接提供了⼀种⽅法,使进程可以调⽤不属于其可执⾏代码的函数。

函数的可执⾏代码位于⼀个DLL 中,该DLL 包含⼀个或多个已被编译、链接并与使⽤它们的进程分开存储的函数。

在vs中新建⽣成动态库的⼯程,编译成功后,产⽣⼀个.lib⽂件和⼀个.dll ⽂件
那么上述静态库和动态库中的lib有什么区别呢?
静态库中的lib:该LIB包含函数代码本⾝(即包括函数的索引,也包括实现),在编译时直接将代码加⼊程序当中
动态库中的lib:该LIB包含了函数所在的DLL⽂件和⽂件中函数位置的信息(索引),函数实现代码由运⾏时加载在进程空间中的DLL提供
总之,lib是编译时⽤到的,dll是运⾏时⽤到的。

如果要完成源代码的编译,只需要lib;如果要使动态链接的程序运⾏起来,只需要dll。

以下例⼦均在vs2010上测试
⽣成和使⽤动态库
⽣成动态库
新建项⽬--win32项⽬--填写项⽬名--确定--下⼀步--应⽤程序类型:选择dll--附加选项:选择导出符号--完成
可以看到⽣成了⼀个dllmain.cpp ⽂件,这是dll应⽤程序的⼊⼝,注意它和普通⼯程的⼊⼝main函数不同,这个⽂件我们不需要修改。

在这个动态库中我们举例导出⼀个变量,⼀个类,⼀个函数,头⽂件dll.h如下:
1//新建⽣成dll的⼯程时,vs默认定义了宏DLL_EXPORT,因此,DLL_API 是
__declspec(dllexport),⽤来导出
2//当我们在静态调⽤dll时,我们包含该头⽂件,由于没有定义DLL_EXPORT,所以DLL_API 是
3//__declspec(dllimport),⽤来导⼊
4 #ifdef DLL_EXPORTS
5#define DLL_API __declspec(dllexport)
6#else
7#define DLL_API __declspec(dllimport)
8#endif
9
10// 导出类
11class DLL_API Cdll {
12public:
13Cdll(void);
14// TODO: 在此添加您的⽅法。

15 };
16
17//导出变量,变量在.cpp⽂件中定义
18extern DLL_API int ndll;
19
20//导出函数,加extern "C",是为了保证编译时⽣成的函数名不变,这样动态调⽤dll时才能21//正确获取函数的地址
22extern"C" DLL_API int fndll(void);
dll.cpp ⽂件如下:
1 #include "dll.h"
2
3
4// 这是导出变量的⼀个⽰例
5 DLL_API int ndll=6;
6
7// 这是导出函数的⼀个⽰例。

8 DLL_API int fndll(void)
9 {
10return42;
11 }
12
13// 这是已导出类的构造函数。

14// 有关类定义的信息,请参阅dll.h
15Cdll::Cdll()
16 {
17return;
18 }
调⽤动态库
有两种⽅法调⽤动态库,⼀种隐式链接,⼀种显⽰链接。

调⽤动态库:隐式链接
隐式链接需要.h⽂件,dll⽂件,lib⽂件
(1)将dll放到⼯程的⼯作⽬录
(2)设置项⽬属性--vc++⽬录--库⽬录为lib所在的路径
(3)将lib添加到项⽬属性--链接器--输⼊--附加依赖项(或者直接在源代码中加⼊
#pragma comment(lib, “**.lib”))
(4)在源⽂件中添加.h头⽂件
然后就像平常⼀样调⽤普通函数、类、变量
调⽤动态库:显⽰链接
显⽰链接只需要.dll⽂件,但是这种调⽤⽅式不能调⽤dll中的变量或者类(其实可以调⽤类,但是相当⿇烦,有兴趣者可参考
/doc/8c1481822.html
/jdcb2001/article/details/1394883)
显⽰调⽤主要使⽤WIN32 API函数LoadLibrary、GetProcAddress,举例如下:
1typedef int (*dllfun)(void);//定义指向dll中函数的函数指针
2 HINSTANCE hlib = LoadLibrary(".\\dll.dll");
3if(!hlib)
4 {
5std::cout<<"load dll error\n";
6return -1;
7 }
8dllfun fun = (dllfun)GetProcAddress(hlib,"fndll");
9if(!fun)
10 {
11std::cout<<"load fun error\n";
12return -1;
13 }
14fun();
⽣成和使⽤静态库
⽣成静态库
新建项⽬--win32项⽬--填写项⽬名--确定--下⼀步--应⽤程序类型:选择静态库静态库项⽬没有main函数,也没有像dll项⽬中的dllmain。

创建项⽬后添加.h⽂件,添加相应的导出函数、变量或类,如下所⽰
1 #ifndef _MYLIB_H_
2#define _MYLIB_H_
3
4void fun(int a);
5
6externint k;
7
8class testclass
9 {
10public:
11testclass();
12void print();
13 };
14
15#endif
.cpp⽂件如下
1 #include "stdafx.h"
2 #include "lib.h"
3 #include
4
5void fun(int a)
6 {
7std::cout<
8 }
9
10int k = 222;
11
12testclass::testclass()
13 {
14std::cout<<"123\n";
15 }
16
17void testclass::print()
18 {
19std::cout<<"this is testcalss\n";
20 }
编译⼯程后就会⽣成⼀个.lib⽂件使⽤静态库
需要.h⽂件,lib⽂件
(1)设置项⽬属性--vc++⽬录--库⽬录为lib所在的路径
(2)将lib添加到项⽬属性--链接器--输⼊--附加依赖项(或者直接在源代码中加⼊#pragma comment(lib, “**.lib”))
(3)在源⽂件中添加.h头⽂件
然后就像平常⼀样调⽤普通函数、类、变量,举例如下:
1 #include
2 #include "lib.h"
3
4#pragma comment(lib, "lib.lib")
5
6int main()
7 {
8fun(4);
9std::cout<
10testclasstc;
11tc.print();
12return0;
13 }
【版权声明】转载请注明出
处/doc/8c1481822.html
/TenosDoIt/p/3203137.html
三、字符集设置
VC真是⼀个⾮常笨,⾮常不友好的⼯具,还是这样说,VC(MFC)和现在流⾏的。

net framework java⽐起来就想⽯器时代跟⼯业时代相⽐⼀样!
接触MFC也有⼏年了,为了它有过加班、有过熬夜、甚⾄通宵,代码没有⼗万⾏也应该有⼏万⾏了。

但是MFC就是这么⽜,它⽜得不但令新⼿忘⽽却步,⽽且常常令有经验的软件⼯程师也栽跟⽃。

最近由于⼀个⼩⼩的环境设置设置问题花了很多时间,这跟⽤惯了VC6
突然转到VC2005有关,但关键还是VC实在太笨了,它让我在⼀周内连续两中招次!
第⼀次中招是这样的,很简单:
我不知道/doc/8c1481822.html
2005默认⼯程默认设置是采⽤“Unicode字符集”(Unicode Character Set)的,以前⽤VC6⼯程的时候默认是“多字符
集”(Multi-Byte Character Set)的。

以前也没有⽤过/doc/8c1481822.html
2005啊,我⼀直认为。

net是⽤来在framework上⾯编程的,在MFC 上编程没有必要打开庞⼤的。

net2005,把机器弄得像⽜拉车⼀样。

我声明了⼀个CString,按计划给它赋值,就像下⾯:
CString s;
s.Format(“count = %d”,count);
按经验这肯定不会有错误的,但是不好意思,编译错误,因为这是我的环境采⽤的Unicode字符集的,⽽我给CString的Format函数是“多字符集”(Multi-Byte)所以编译不通过,要知道在这种设置下使⽤MessageBox(“ddd”);编译是不会通过的,因为系统调⽤的是MessageBoxW,即Unicode宽字符集的那个函数。

还好我根据编译器的提⽰把s.Format(“count = %d”,count);改成s.Format(_T (“count = %d”,count);就搞定了,_T 代表⼀个宏,宏的意思就是把字符串转成宽字符表⽰。

同样的,MessageBox(“ddd”);可以为
MessageBox(_T(“ddd”));
但是还有个问题就是,所有窗体显⽰的东西都是宽字符的,例如a在内存⾥就是a两个字节,前⾯⼀个字节a后⾯是,当从窗体取下数据(例如⽤户输⼊)要跟其他平台交互时,例如⽹络传输到远端机器。

如果那边使⽤的不是Unicode字符集,就会出问题,为了使界⾯和后台传输⼀致,只好使⽤把宽字符转换成多字符集表⽰:
CStringstrWideChar;
strWideChar.Format(_T(“这是宽字节哦”));
char buf[20];
memset(buf,0,20);
WideCharToMultiByte( //转换Unicode到Ansi
CP_ACP,
WC_COMPOSITECHECK | WC_DEFAULTCHAR,
strWideChar,
strWideChar.GetLength(),
(char *)buf, //转换到缓冲区中
20, //最多个字节
0,
);
同样的,你接收到的字符串想要在界⾯正常显⽰,还必须把它转换成宽字节表⽰:
char chBytes[8];
memcpy(chBytes,”aaaaaaa”,8);
WCHAR wch[9];
n = MultiByteToWideChar( //转换Unicode到Ansi
CP_ACP,
0,
chBytes,
8,
wch, //转换到缓冲区中
8 //最多个字节
);
wch[n] = '';
这样每次从界⾯取数据和把数据显⽰到界⾯上都要先做处理,但是也可以把编译环境设置成“多字符集”(Multi-Byte Character Set),就可以避免这样转换来转换去(可惜我发现的时候代码已经差不多写完了)。

就是在“Project->Configuration Properties->General->Character Set,选择”Use Unicode Character Set“就是使⽤Uncode 字符集,选择” Use Multi-Byte Character Set“就是多字节字符集。

第⼆次中招,god,花了我好长时间才找到问题:
我在CodeProject上找了⼀个很厚道的⽼外写的⼀个继承了CDialog窗体类CResizableDialog的源码,这个类的作⽤是使MFC 的窗体放⼤缩⼩时,窗体上的控件可以定位(Auchor),不要⼩看这个⼩⼩的每天都要⽤到的功能,⽤MFC实现真的很⿇烦。

很佩服那个⽼外写了那么多代码(当然跟他们的条件有关,资本主义国家的⼯⼈随便找个⼯作就可以⾐⾷⽆忧,病了政府照顾,我们做“挨踢”的活得像民⼯⼀样,当然没有那个闲情去写那么好的代码免费给别⼈使⽤,这是题外话)。

我拿了那个现成的⼯程,直接在我的⼯程⾥引⽤他的⼯程。

Everything works perfect.直到我把项⽬发布成Release的,双击运⾏后没有任何反应,Very weird!后来我⽤MessageBox打印消息,发现运⾏到DoModal函数⾥⾯就没有出来,程序直接退出了!使⽤try,catch都得不到错误!因为我的窗体是继承⽼外写的窗体类来的,原先继承CDialog 是好好的,问题肯定在他的⼯程⾥⾯,可是他给的⽰例程序没有任何问题啊。

MFC出错的时候是很要命的,它不会给你任何提⽰,它就是不⼲了!
我⼜拿⼀个前的测试程序,让它从CResizableDialog继承,也没有任何问题。

简直头⼤了、⽆语了,不知道哪⾥出现了问题,Release⼜不能像Debug那样调试,打了⼀堆MessageBox后还是不知道问题出现在哪⾥。

凭着经验,可以知道程序中可能出现了内存的越界访问什么的致命错误,才会导致程序“⼀声不吭”地退出,但是究竟哪⾥出了问题呢?
就在束⼿⽆策的时候,我发现调⽤CResizableDialog的成员函数EnableSaveRestore 会引发链接错误:“未定义的外部符号”,不引⽤它不会出错,测试程序引⽤它没有任何错误。

通常这个错误造成是因为引⽤函数在。

h⽂件⾥声明了,但是在。

cpp⾥⾯没有定义,或者。

cpp⽂件⾥的定义和。

h上的参数对不上。

但是此时不可能是这个错误,因为测试程序没有错误啊。

直觉告诉我这是解决“Release后程序直接退出的关键”,说不定这个函数调⽤的问题解决了Release的问题也解决了。

MFC真是很强⼤,它强⼤得不但“像迷宫⼀样,⾥⾯有怪兽,进去⼀不⼩⼼就永远出不来”,⽽且它让你当遇到怪兽的时候总是给你⼀点点星光,只要你不放弃,奇迹就会出现,你就会练成绝世神功。

这跟武侠⼩说是相通的,主⼈公每次到了⽣死关头就会出现奇迹,成为天下⽆敌的⾼⼿。

看看我怎么找到解决⽅法的,Very tricky。

既然调⽤EnableSaveRestore出现了不该出现的错误,那么就从这个函数开始找。

这个函数是这样的:
.h⽂件声明
void EnableSaveRestore(LPCTSTR pszSection, BOOL bRectOnly = FALSE);
.cpp⽂件定义
void CResizableDialog::EnableSaveRestore(LPCTSTR pszSection, BOOL bRectOnly/* = FALSE */)
{
m_sSection = pszSection;
m_bEnableSaveRestore = TRUE;
m_bRectOnly = bRectOnly;
// restore immediately
LoadWindowRect(pszSection, bRectOnly);
}
上⾯的代码没有任何错误,既然没有错误,就要⽤使⽤以下⽅法来找:
1.重新为CResizableDialog写⼀个函数,它没有参数的,调⽤它,发现没有错误,看来参数有问题。

2.既然没有参数的函数没有错误,就把出问题的函数参数去掉吧,竟然也没有错误!那问题就肯定是出在参数上。

3.去掉其中⼀个参数,测试发现是LPCTSTR pszSection的问题,⽽不是BOOL bRectOnly 的问题。

4.既然这样,那就换⼀种表⽰吧,把LPCTSTR pszSection换成WCHAR* pszSection,运⾏它,竟然不出错了!翻开MFC宏定义,就会发现其实LPCTSTR和WCHAR*是⼀样的,MFC 真是freak!
5.但是这个函数功能还是不正常,断点进⼊那个函数⾥⾯发现传进去的字符串只有⼀个字符了,这种情况就是宽字符当成短字符时,第⼆个字节的当成了字符串的截⽌字符了,也就是说,这个函数⾥采⽤的是短字符(多字符集Multi Byte)处理的。

6.我的⼯程采⽤的是宽字符集(Unicode Char)的,检查设置,原来那个⽼外是⽤VC6编的,默认是使⽤多字符集(Multi Byte)的,VC真是笨啊,两个Project在⼀个Solution ⾥⾯完全不同的设置竟然没有任何提⽰,简直把我弄死了!
7.把引⽤⼯程也改成使⽤Unicode字符集,并且把函数EnableSaveRestore WCHAR* pszSection恢复原样,搞定!果然不出我所料,Release也没有问题了!我⽤以前的那个测试程序来使刚好以前把它设成Multi Byte,所以也没有错误,Damn!
仅仅是⼀个设置啊,如果VC出错提⽰稍微好的,⾄少字符集不匹配不要说成“未定义的外部符号”也好⽤⼀点啊,难怪现在⽤VC的⼈越来越少了!
注:通常说的VC不是指使⽤。

net framework的VC,那个很简单,内存都不⽤管,通常是指⾮托管的VC。

相关文档
最新文档