MicrosoftVisualC++(简称VisualC++、MSVC、VC++或VC),是微软公司的C++开发工具,具有集成开发环境,可编辑编译C、C++及C++/CLI等语言。VC++整合了便利的排错工具,特别是整合了视窗应用编程接口(WindowsAPI)、三维动画DirectXAPI及Microsoft.NET框架。目前最新的版本是VisualC++2010。
VisualC++一直以来都是VisualStudio系列开发工具套件的重要成员。VisualStudio是微软公司推出的著名产品,是目前最流行的Windows平台应用程序开发环境。VisualStudio2010(简称VS2010)于2010年4月12日上市,其集成开发环境(IDE)的界面被重新设计和组织,变得更加简单明了。VisualStudio2010集成了VisualC++2010。
本书使用网上下载的VisualStudio2010安装包:
cn_visual_studio_2010_ultimate_x86_dvd_532347.iso这是个镜像(.iso)文件,需要虚拟光驱才能运行。用虚拟光驱软件DAEMONToolsLite载入镜像,如图1.1所示。
图1.1载入VS2010安装.iso文件
载入后弹出如图1.2所示的启动窗口,单击“安装MicrosoftVisualStudio2010”,进入如图1.3所示的安装向导界面,单击“下一步”按钮继续。
图1.2启动窗口
图1.3VisualStudio2010旗舰版安装向导
在图1.4所示窗口中选择“我已阅读并接受许可条款”,在图1.5所示窗口中选择“完全”,产品安装路径为默认的“C:\ProgramFiles\MicrosoftVisualStudio10.0\”,单击“安装”按钮开始安装进程。
图1.4接受安装许可条款
图1.5选择安装路径
图1.6安装进行中
图1.7设置为VisualC++的开发环境
VisualStudio2010的VisualC++集成开发环境的起始界面如图1.8所示。
图1.8VisualC++2010集成开发环境
读者也可从网络获得VisualStudio2010的可执行(非镜像)安装程序或者直接从光盘安装。
下面以编写一个简单的演示程序为例,使读者初步熟悉VisualStudio2010环境下VisualC++开发的基本操作。
选择菜单命令“文件”→“新建”→“项目”,如图1.9所示。
图1.9新建项目
系统弹出“新建项目”对话框(如图1.10所示),左边“项目类型”树中默认选项为“VisualC++”→“MFC”,对应右边“模板”选择“MFC应用程序”,给项目命名为“GetIPAndPort”(我们即将做的这个软件是用来演示程序如何获得用户输入的IP和端口号的——这也是几乎所有网络程序都具有的功能,所以取这个名字)。
图1.10项目命名
单击“确定”按钮,弹出“MFC应用程序向导”对话框(如图1.11所示),接下来我们将在这个对话框的指引下轻松完成创建VC工程的工作,单击“下一步”按钮继续。
图1.11MFC应用程序向导
在“应用程序类型”界面(如图1.12所示)选中“基于对话框”单选按钮(这个程序很简单,用不着文档和视图),取消选择界面下方的“使用Unicode库”复选框(在本书所有程序建立工程的时候都要记得这一步,为程序兼容性考虑,避免字符串处理的麻烦),单击“下一步”按钮。
图1.12选择应用程序类型
接下来的“用户界面功能”和“高级功能”界面(如图1.13所示)都采用系统默认设置,连续单击“下一步”按钮跳过。
图1.13“用户界面功能”和“高级功能”默认设置
最后一步出现的是“生成的类”,稍留意下可以看到,系统已经自动为程序建立了两个类——CGetIPAndPortApp和CGetIPAndPortDlg(如图1.14所示),其中CGetIPAndPortApp类代表应用程序本身,CGetIPAndPortDlg类代表程序的主界面对话框。细心的读者可能会发现,这两个看似冗长的类名其中间部分“GetIPAndPort”就是我们刚才取的工程名!没错,以后大家就会发现一个规律:VC在一开始创建工程时都会默认生成两个类,名称形如CXXXApp和CXXXDlg(其中“XXX”部分就是用户指定的工程名),这样的命名法则是为了方便用户理清程序的类结构。
图1.14生成的类
单击“完成”按钮,至此一个VC工程就创建完成了。
开发环境工作区主界面将呈现的样子如图1.15所示。
图1.15开发环境工作区主界面
主工作区大致分为三部分,最左边是供用户浏览程序结构的,包括好几个选项卡界面,常用的是解决方案资源管理器、类视图和资源视图,如图1.16所示。
图1.16三个常用的视图
类视图用树状结构展示了工程中所有C++类及其层次结构,单击类名可在VisualStudio2010环境界面右下角的“属性”窗口中设置对应的类,包括为其添加新的事件消息,重写某些方法的实现代码等。
资源视图分类列出了程序的所有资源,其中常用的是Dialog资源,这种资源是每一个具有GUI的程序都有的,双击资源ID号可以打开对应的界面设计工作区,就可以设计程序的图形界面了。
VC在创建工程时默认会生成两个类(本例是CGetIPAndPortApp和CGetIPAndPortDlg),图1.16解决方案资源管理器中的头文件GetIPAndPort.h和源文件GetIPAndPort.cpp对应CGetIPAndPortApp类,它们一同构成了该类的源代码实现;同理,GetIPAndPortDlg.h和GetIPAndPortDlg.cpp对应CGetIPAndPortDlg类,从中间类视图中也可以看到这两个类。
除此之外,读者会发现类视图里多了一个CAboutDlg类,它是VC自动创建的,叫做“关于GetIPAndPort”对话框,用来显示程序的版本信息。
在本例的三个类中,CAboutDlg类和CGetIPAndPortDlg类都有各自的对话框界面资源。资源视图中Dialog目录下有它们的ID号(对应的分别是IDD_ABOUTBOX和IDD_GETIPANDPORT_DIALOG),双击ID号可以打开其对话框的界面设计工作区,如图1.17所示。
图1.17“关于GetIPAndPort”对话框的设计工作区
工作区中显示的是“关于GetIPAndPort”对话框的默认界面,可以在此基础上自己设计或重新布局。
解决方案资源管理器、类视图和资源视图三者密切配合,将程序代码有机地组织成一个结构精巧的C++项目,通过这三者可以清楚地看到程序中每个类的对应代码实体,用C++写的类不再是抽象的代码,而是成为看得见、摸得着的现实存在,这就是可视化设计的魅力!
对话框界面的设计布局如VisualBasic一样方便:开发环境界面中央是主工作区,在这里可以打开任意多个程序文件(源文件或头文件)及对话框界面设计工作区;只要将右边工具箱中的控件直接拖曳到工作区中,就可以设计出自己想要的程序界面。VC环境下的工具箱和VB的一样,都包含了一般Windows程序通用的界面元素(按钮、文本框、静态标签、列表框、滚动条等),用过VB的人对这些常用控件肯定再熟悉不过了,故这里不做过多介绍。只是针对本书所介绍的网络编程,有一个控件需要特别提一下,那就是IP地址控件(如图1.18所示)。它在界面上的显示效果如图1.19所示。
图1.18IP地址控件
图1.19地址控件的显示效果
稍后将会介绍这个控件的具体用法,读者会看到它是个很实用的控件。
选择工具箱中的控件设计程序界面,可以看到VC界面设计环境的使用极其方便,丝毫不比VB逊色。在布局界面时可以使用工具栏中提供的功能调整各个控件的大小、对齐方式。如图1.20所示,先选择“关于”按钮控件,再选择“退出”按钮控件,然后单击工具栏中的“使大小相同”按钮,就可以使先选择的按钮与后选择的按钮大小一样。
图1.20调整控件的大小
最终设计出的程序界面效果如图1.21所示。
图1.21程序界面
在网络编程中,程序使用IP和端口来标识网络上的其他程序,以实现程序之间的通信(进程通信),因此获取并正确处理对方进程的IP和端口(通常由用户通过界面输入指定)就成为网络程序的通用功能。下面的小程序就用来演示这个最基本的功能,不过,它只负责获取和处理IP、端口号,并不实际发起网络连接。
这个程序的界面已经设计好了,但要让程序完成一定的功能,还必须为其编写代码。写代码之前,首先要定义程序中需要用到的一些变量。在VC中,很多变量都不是孤立的,而是与某个界面元素(即控件)绑定的,这样用户在界面上的输入就可以很容易地传递给程序中相应的变量进行处理。例如,为了在程序代码中获得用户输入的IP,需要给IP地址控件关联一个变量。如图1.22所示,右击该控件,在弹出的菜单中选择“添加变量”命令。
图1.22给控件关联变量
出现“添加成员变量向导”对话框(如图1.23所示),将变量命名为“m_ip”,变量类别为“Control”。
图1.23添加Control型变量
“Control”表示控件变量,它是微软对实现Windows程序的图形用户界面元素的一些类(类名如CEdit、CButton)的总称。这些类大多是由同一个叫做窗口(CWnd)的C++类衍生过来的,CWnd及其庞大的衍生类家族封装了Windows的GUI,作为MFC的重要组成部分,是微软对基础C++语言的扩充,使其具有了强大的图形化界面功能。与CWnd家族中的类关联的变量即控件变量,可以通过这样的变量获取和控制它所关联控件的几乎一切行为,从而对程序界面进行灵活的编程和定制。
而那些仅仅只是获取用户输入值的变量称为值类型(Value)变量,这种变量与我们以前上C++语言课时接触的那些变量差不多,所不同的只是它们都有各自关联的控件,用于获取和保存特定控件接收的用户输入。
除了Control和Value这两大类变量外,用户在编程时也可以根据需要自定义临时变量,这种变量就是普通C++里用的那些变量(整型、实型、字符型之类)。
下面接着添加变量,给用于接收端口号的文本框关联Value变量strport(如图1.24所示)。
图1.24添加Value型变量
再设置该文本框的Number属性为True(如图1.25所示),之所以这样设置,是为了限定用户只能在这个文本框中输入数字形式的端口号。这样设置之后,后面运行程序时读者会发现:如果试图输入非数字字符(中文、英文字母),则文本框一概不响应,也就杜绝了用户的非法输入。
图1.25设置文本框Number属性
本程序还有一个文本框是用于显示程序获取的IP和端口的,给它关联Control型变量m_showIpAndPort,并且设置ReadOnly属性为True(作为显示信息窗口的文本框一般都设为只读模式),如图1.26所示。
图1.26设置显示框ReadOnly属性
至于给控件关联的变量究竟是设成Control还是Value型,应视具体需要而定。一般来说,如果希望对控件的行为进行某种控制,设置变量为Control型较好;如果仅仅想获得控件接收的用户输入值,设置为Value型就足够了;有时候对控件的行为有较高的控制要求,同时又希望能够非常方便地获取控件的输入值,也可以对一个控件同时关联定义这两类变量。
现在,所有的变量都关联好了,界面控件也都进行了恰当的设置和布局,只剩下编码工作了。程序界面上的“获取”按钮是实现本程序功能的关键,编程工作主要就是给这个按钮添加事件处理程序。右击“获取”按钮,在弹出的功能菜单中选择“添加事件处理程序”命令,如图1.27所示。
图1.27给“获取”按钮添加事件处理程序
在“事件处理程序向导”对话框中将这个处理程序命名为“OnShowIpAndPort”,如图1.28所示。
图1.28给事件处理程序命名
单击“添加编辑”按钮,进入代码编辑窗口(如图1.29所示),VC自动打开需要编辑的程序代码文件GetIPAndPortDlg.cpp并定位到源文件中相应的位置,供用户添加自己的代码。以后在编译、调试程序时,也可随时先选中程序界面上的“获取”按钮,然后再在开发环境右下角“获取”按钮的属性设置窗口中选择这个事件处理过程。这样就可以随时随地定位到这个事件过程的源码编辑处,修改完善程序。需要指出的是,事件过程OnShowIpAndPort的代码必须填写在如图1.29中VC为我们指定的地方,写在其他任何地方或本工程的其他代码文件中都是无法运行的。
图1.29编辑事件处理过程代码
为“获取”按钮编写的事件过程代码如下:
BYTEnFild[4];//分别存放IP地址的四个字段CStringsip;//IP地址的字符串形式(可以直接显示在界面上的)UpdateData();//刷新对话框界面,获取用户输入//验证输入是否合法if(m_ip.IsBlank())//若用户没有填写IP地址,则提示填写{AfxMessageBox("请填写IP地址!");return;}if(strport.IsEmpty())//若用户忘了指定端口号,则提醒其指定{AfxMessageBox("请指定进程端口!");return;}//获取用户输入的IP地址值m_ip.GetAddress(nFild[0],nFild[1],nFild[2],nFild[3]);//将IP地址格式化为可以在计算机屏幕上显示的字符串sip.Format("%d.%d.%d.%d",nFild[0],nFild[1],nFild[2],nFild[3]);//在界面上显示用户输入的网络进程地址(包括所在主机的IP和端口)m_showIpAndPort.SetWindowTextA(sip+":"+strport);m_ip.SetFocus();//焦点回到IP地址栏双击“关于”按钮,为其添加事件过程(如图1.30所示)。VC也会自动定位到需要添加代码处,事件处理过程函数自动命名为OnBnClickedButton2,表示在单击Button2(即“关于”按钮)时执行这个事件过程。
图1.30添加“关于”按钮事件过程
“关于”按钮的Click事件代码如下:
CAboutDlgdlg;dlg.DoModal();//显示“关于”对话框由此可见,添加代码有两种方式:一种方式是右击控件后选择“添加事件处理程序”菜单项,另一种方式是直接双击该控件。所不同的是第一种方式可以自定义事件处理程序的函数名,如本例命名“获取”按钮的单击处理过程函数名为OnShowIpAndPort,这是个有意义的名字,表示单击“获取”按钮后,程序就会显示(Show)指定的“IP”和“端口(Port)”;而采用第二种方式添加的函数使用系统自动生成的函数名,如OnBnClickedButton2,虽然这个名称也有一定意义,表示事件过程是在单击(BnClicked)第二个按钮(Button2)时发生的,但并没有直观地表示出它要完成的功能。
对于小程序的开发,这两种方式都是可行的,并没有本质区别。但是如果程序规模稍大,事件处理过程较多的时候,就要注意了:采用第一种方式添加的函数由于其名称更有意义,将使得最终整个源程序的可读性很强,便于修改、维护,因此对于程序的重要功能函数尽量使用第一种方式添加;而第二种方式也不是完全没有用——对于一些不重要的函数(并非程序必要的核心功能),若都采用第一种方式添加,都要给它取一个有意义的名字,势必给编程工作造成不便,而且过多地增加有意义的函数名也容易造成命名混乱,反而降低了程序的可读性。在实际编程中,要根据具体情况灵活运用这两种方式。
至此,这个简单的VC小程序就编写完成了,下面来运行。
图1.31启动程序界面
我们先不填写IP地址而直接单击“获取”按钮,看看会发生什么。程序弹出了消息框,提醒填写IP地址(如图1.32所示)。
图1.32提醒填写IP地址
接下来填写IP地址时,我们故意将最后一个字段填成300(或任何其他大于255的十进制数),这时你会发现:IP地址控件会自动将它重置为255(因为值大于255的IP地址字段是不合法的),而且始终无法在IP地址控件中输入其他非数字字符(这就是使用IP地址控件的好处)。如果使用其他控件接收用户输入,用户就要自己编写代码来检查输入的合法性,IP地址控件将这种烦琐的验证过程封装起来自动完成,给编写程序带来了极大的方便。
填写完合法IP,再故意不填端口号,单击“获取”按钮后,程序同样也会弹出消息框,提醒输入端口号(如图1.33所示)。
图1.33提醒填写端口
在IP和端口都合法填写的情况下,单击“获取”按钮,程序就会将用户输入的网络进程地址(IP+端口)显示在下方的输出文本框中,如图1.34所示。
图1.34显示运行结果
这样,程序就完成了对用户输入网络进程地址的获取工作,在本书以后的程序示例中,这样的操作会经常用到。在获取了进程地址后,程序会进一步用这个地址作为参数传递给不同的网络编程接口函数,从而完成丰富的网络功能。
图1.35“关于GetIPAndPort”对话框
通过以上这个简单的小程序,读者已经初步熟悉了本书要使用的开发平台,但要进行网络编程还必须对一些基本概念有所了解,下面就来介绍。