小斯同学花了几周的时间,终于把我们的服务端和客户端从vs2005升级到vs2013了。真是不得不给个赞。
升级的过程中遇到了各种问题,小斯同学跋山涉水、越过艰难险阻终于成功让我们用上了高大上的宇宙第一IDE——vs2013。所以这里我顺带把他升级中遇到的问题记录一下,也许对一些朋友是种参考。
首先,需要说明一下,之前在csdn上看到有人提了一个问题:为什么好多游戏都用VS2005开发呢?原帖子在这里:http://bbs.csdn.net/topics/390645505
下面的回复也是五花八门。这里作为从业人员,我谈一下感受,首先用vs2005还是用vs2012甚至vs2013只是一个选择问题,换句话说,只要愿意,我们可以拿什么乱七八糟的玩意开发都没问题。
再来说下实际的,为什么大多数游戏公司都用vs2005开发呢?我其实不知道那个楼主说魔兽世界和愤怒的小鸟是用vs2005开发的,但我们的游戏确实是拿vs2005开发的,而且我知道的一些游戏公司也确实拿vs2005开发,那么为什么呢?因为项目就是在那个时候创建的,而那个时候正好手头上熟悉的开发工具就是vs2005,愤怒的小鸟2009年发行的第一个版本,其开发公司Rovio公司成立于2003年,他们公司之前当然不会打酱油了,所以目测之前我们用的不是vs2005,因为那个时候还没有vs2005呢,估计有了vs2005之后,他们公司升级到vs2005作为开发工具了,而vs2008要等到07年底才出来,如果他们的程序们不想升级,自然是没人会去升的,而魔兽世界我们都知道是04年就出了,所以那个时候你总不能说他们的开发工具是vs2005吧。所以魔兽如果真的在用vs2005,那也是之后升级到vs2005的。至于为什么不继续升级到vs2008或者是vs2010、12、13等,我的看法是,没有需求。公司的目的是盈利,而如果从vs2005升级到vs2013无法提高收益,那么为什么要升级,既然公司不会为你升级而发奖金或者是工资,自然没人闲的蛋疼去升级,还要处理一堆的升级之后的问题。
还有一点原因,因为用户看到的那个程序并非是由程序员来“控制”用什么编译的,当然这里的控制是打引号的,听我慢慢解释,在公司升级开发工具并非跟自己玩玩一样简单,首先,一般公司编译代码都是配有编译机的,就是说程序员在自己本地用开发工具来编写调试代码之后通过各种版本控制工具,比如svn或者git之类的提交到代码库里,然后由编译机来编译出真正的供使用的版本,所以,如果你想升级开发工具就要同时升级自己的开发工具和编译机的编译配置,一般而言,公司的编译机都有专门的人来管理的,也就是所谓的配置管理员之类的职位,他们负责管理编译机,程序员当然可以在本地用各种开发工具进行开发,只要你能够保证代码编译没有问题,谁管你是用vs2013开发还是用记事本在写代码?但是程序员如果想要控制输出到外网的版本是用什么编译工具来编译的话,就要说服配置管理员来升级编译机上的配置,而一般公司而言,这都是不可能的,因为程序员压根管不到配置管理员,如果不是项目经理点头首肯,配置管理员为什么要听你程序员的,而程序员如果要说服项目经理,那么就要摆出各种厉害关系,比如vs2013如何碉堡(谁管你碉不碉堡)、如何能提高开发效率(尼玛不升级你效率就低下吗?)、vs2013能够给用户带来飞一般的体验(即使你信口开河,项目经理也不一定信)。即使你口才非凡的说服了项目经理,他同意让公司花一笔钱把新的IDE买下来供大家使用,你还需要说服其他的程序员,因为你一旦修改了项目的编译工具,必然要影响到其他程序员的开发,当然我们必须能够说服其他程序员,让他们改变开发习惯。总结一下:
1、你需要说服项目经理,让他申请花费一笔钱购买新的vs
2、你要说服配置管理员,让他更改编译机的配置,很有可能你需要帮助他解决新配置下的各种问题,而也许你并不擅长
3、你需要说服别的程序员,让他们改变开发习惯,适应新的开发工具
4、最后你需要解决你们项目工程由vs2005升级到vs2013的各种诡异bug,保证升级之后你们的用户不会在使用上有问题。
当然,这是从程序员推动去升级开发工具,如果从项目经理推动的话,就容易的多了,一句话“你们都给我用vs2013开发”,其他的事情自然有人去完成了,程序员要做的不过是安装一下vs2013罢了。但是有多少项目经理是真正关心程序员拿什么开发,又或者程序最后拿什么编译的呢?呵呵!其实只有程序员关心这些。我相信所有跟我一样的程序员都是喜欢尝试新鲜事物的,所以很多情况下,你虽然看到的程序是用vs2005编译的,实际上我很有可能是用着vs2013在做开发,只不过编译给你的是vs2005编译的罢了:)
回到正题,我们项目升级过程中遇到了哪些问题呢?这里我分别挑几个显著的问题说一下:
工程自动转换
首先vs2013对vs2005的工程是兼容的,也就是说你可以直接用vs2013打开vs2005的工程,当然,vs2013会对vs2005的工程文件做一番转换。找到vs2005的sln文件,用vs2013打开完成转换后又日志报告,一般来说不会有什么问题,如果你想仔细看也可以,点进去看就是的:
这里要注意一点,在转换sln的时候,我们的vs2013挂掉了,最后我们发现是有一个工程的vcproj文件里有引号字符的html代码,这个在vs2005下是可以识别的,但是用vs2013去转换它的时候却把vs2013给弄死掉了,去掉就可以了
工程设置上的不同
还有一点就是在vs2005和vs2013工程配置里有好多默认设置居然是不同的,比如输出目录,vs2013的路径最后会添加一个 ‘\’,而vs2005下却没有。这个最初看上去没什么影响,但是我们的工程自己定义了路径宏的时候添加了末尾的符号‘\’,导致在vs2013下路径就多了一个‘\’。
解决方案打开后一般来说各个工程的组织结构不会变动,这个时候不要紧着对整个解决方案进行编译,建议一个工程一个工程的编译,这样解决问题起来比较清晰。
vs2013和vs2005另一个比较大的变化就是VC++的目录设置,在vs2005里面这个目录设置是针对所有工程的总设置,在Tools->Options->Projects and Solutions->VC++ Directories下,而在vs2013里,这个设置放到了每个工程设置下,在Project->Properties->VC++ Directories下,位置变化到是次要的,问题是vs2013把很多头文件放到了系统的一个目录,而且文件内容还发生了变化。
系统宏的不同
比如我们用到了一个系统的宏:OutputDebugStr,它在vs2005的头文件里定义在vs安装目录下的平台sdk目录下的mmsysytem.h,而到vs2013下这个文件被放到了系统目录的sdk下,而且这个宏的定义还消失了。
解决办法也比较简单,在工程的预编译文件里添加一下这个宏的定义,注意兼容vs2005和vs2013版本就行:
#ifndef OutputDebugStr #define OutputDebugStr OutputDebugString #endif
其他未定义宏的解决方案也类似。只有有一类属于宏的定义值本身就不同,比如,我们遇到这么一个问题:
这个变量是定义在wingdi.h文件中的,但是在vs2013新的sdk下和vs2005的windows的sdk下这两个定义是不一致的,vs2005下是这么定义的:
vs2013下是这么定义的:
目前我们的解决方案只是重新定义一下这个宏:
#if (_WIN32_WINNT >= _WIN32_WINNT_WINXP) #define CLEARTYPE_QUALITY 5 #endif
事实上,vs2013下windows的SDK版本和vs2005自带的SDK版本是有区别的,vs2013的SDK版本是0x0603,而vs2005的版本是0x0500。所以会有一批这种问题出现,其实你可以直接把这个版本号进行修改,这样就可以使用vs2005下的版本内容了。虽然我不知道sdk是否向后兼容,不过应该是兼容的,可以试下。
命名空间的检查更加严格
我们的程序引用了一个以前用C写的库,里面用到了一个结构体名字叫:is_base_of,结果没想到vs2013用的std命名空间里有一个一样的名字,所以我们的解决方案是给我们自己写的结构体加上了一个命名空间。
namespace test { struct is_base_of { ... }; };
dx头文件的不同
我们都知道vs2013已经集成了dx的sdk,而不像之前一样单独发布dx的sdk,同时,vs2013把dx的头文件放到了系统的sdk目录里。在上面我们提到过vs2013和vs2005的VC++目录设置不同,vs2013包含了系统的sdk目录,这导致了一个问题。举个例子就明白了,因为我们的客户端程序用到了dx9,而为了保证版本的一致性,我们把dx9的头文件放到了工程目录当中,在vs2005下,我们不需要特别指明dx的头文件目录,因为系统的默认目录里是没有dx的头文件的,无论我们是用<>还是""的方式来先查找系统目录还是先查找工程目录最后都只能找到我们工程的dx头文件,但是在vs2013下我们必须要把我们自己的dx目录放进工程的VC++目录里,这样才能保证我们引用的是自己的头文件而不是系统的。顺带说一下,VC++目录和在C/C++选项卡下的附加包含目录有什么区别:
配置在VC++目录里的路径,在用 <> 包含的时候会优先查找,而配置在C/C++ 选项卡下的附加包含目录在用 "" 包含的时候会优先查找。
VS自带库的实现不同
这个就比较纠结了,因为两个VS的版本存在差异,难免会出现库的实现不同的情况,比如我们遇到的一个问题:
我们打开两个VS的安装目录下 VC\include queue文件,对比之后发现,这两个queue的实现还真是差别大,我们的问题在于原本是一个public的成员变量,现在变成了protected了:
不过好在变为protected之后,它提供了一个成员函数来获取它,这没办法了,我们只能修改我们自己的代码,把原来直接获取改为用成员函数来获取。