第4讲 Windows编程基础 主讲:谢昕 此为封面页,需列出课程编码、课程名称和课程开发室名称。 要求:每个子课程(6位编码的课程)要求做一个这样的胶片,胶片文件命名为“课程编码 课程名称.ppt”。 此页胶片仅在授课时使用,胶片+注释中不使用。 封面页按产品分为4个,各产品使用自己的封面,把其他封面直接删除即可。
编程的基本概念 与Windows系统密切相关的八个基本概念: 窗口、程序 进程、线程 消息、事件 句柄、API与SDK。
1、窗口 窗口是Windows本身以及Windows 环境下的应用程序的基本界面单位,但是很多人都误以为只有具有标题栏、状态栏、最大化、最小化按钮这样标准的方框才叫窗口。 其实窗口的概念很广,例如按钮和对话框等也是窗口,只不过是一种特殊的窗口罢了。
2、程序 通常说的程序都是指一个能让计算机识别的文件。 接触最多的是以exe或者com作为扩展名的文件。 注意:程序是静态的,进程是动态的。
3、进程 进程就是应用程序的执行实例(或称一个执行程序),进程是程序动态的描述。 一个exe文件,在没有被执行的时候称之为应用程序,当被执行以后,就被操作系统作为一个进程执行了。 当关机或退出,进程便消亡彻底结束了生命。 进程经历了由“创建”到“消亡”的生命期,而程序自始至终存在于你的硬盘上,不管计算机是否启动。
4、线程 线程是进程的一个执行单元,同一个进程中的各个线程对应于一组CPU指令、一组CPU寄存器以及一个堆栈。 进程的动态性,是通过线程来体现的。
5、消息 消息是应用程序和计算机交互的途径,在计算机上几乎做每一个动作都会产生一个消息 鼠标被移动会产生WM_MOUSEMOVE消息,鼠标左键被按下会产生WM_LBUTTONDOWN的消息,鼠标右键按下便产生WM_RBUTTONDOWN消息等等。
关于消息及消息队列 操作系统能够将输入设备的变化上传给应用程序。如用户在某个程序活动时按了一下键盘,操作系统马上能够感知到这一事件,并且能够知道用户按下的是哪一个键,操作系统并不决定对这一事件如何作出反应,而是将这一事件转交给应用程序,由应用程序决定如何对这一事件作出反应。 (好比有个蚊子叮了我们一口,我们的神经末梢(相当于操作系统)马上感知到这一事件,并传递给了我们的大脑(相当于应用程序),我们的大脑最终决定如何对这一事件作出反应,如将蚊子赶走,或是将蚊子拍死。) 对事件作出反应的过程就是消息响应。
在程序运行的过程中改变窗口的大小或者移动窗口等,都会触发相应的“事件”,从而调用相关的事件处理函数。 6、事件 事件是程序对外界动作的反应。 在程序运行的过程中改变窗口的大小或者移动窗口等,都会触发相应的“事件”,从而调用相关的事件处理函数。
7 、句柄 句柄是一个指针,通过句柄就可以控制该句柄指向的对象。 句柄是系统用来标识不同对象类型的工具。 如窗口、菜单等,这些东西在系统中被视为不同类型的对象,用不同的句柄将他们区分开来。
关于句柄 句柄(HANDLE),资源的标识。操作系统要管理和操作这些资源,都是通过句柄来找到对应的资源。 按资源的类型,又可将句柄细分成图标句柄(HICON)、光标句柄(HCURSOR)、窗口句柄(HWND)、应用程序实例句柄(HINSTANCE)等。 操作系统给每一个窗口指定的一个唯一的标识号即窗口句柄。
8、API与SDK API是英文Application Programming Interface 的缩写,意思是“应用程序接口”,泛指系统为应用程序提供的一系列函数接口。 在编程的时候可以直接调用,而不必知道其内部实现的过程,只知道它的原型和返回值就可以了。 SDK是英文Software Development Kit的缩写,意思是“软件开发工具包”,微软提供了许多专门的SDK开发包,比如DirectX开发包和语音识别开发包等等。
网络安全编程概述 从理论上说,任何一门语言可以在任何一个操作系统上编程,C语言可以在Windows下编程,同样也可以在Linux下编程。 编程是一项比较综合的工作,除了熟练使用编程工具以外,还要了解系统本身的内部工作机理和编程语言。
学习Windows下编程 比较流行的是VC++6.0英文企业版 学习语言,选择语言和工具是第一步,而且是非常重要的一步工作,目前的编程语言那么多,有C、C++、C#、Java和汇编语言等等。 虽然有很多语言,只要精通一门就够了。从实用的角度来讲,C/C++是最好的选择,而微软公司的Visual C++和Insprise公司(原Borland公司)的C++ Builder是其相应开发工具的两大主流。 比较流行的是VC++6.0英文企业版
学习编程需要经历三大步 1、读程序 语言基础知识、查相关的资料,补充基础知识 2、写程序 编程贵在动手!要规范、循序渐进。 3、积累功能代码 语言基础知识、查相关的资料,补充基础知识 2、写程序 编程贵在动手!要规范、循序渐进。 3、积累功能代码 分类保存、代码库、效率及正确率。
C语言发展的四个阶段 1、面向过程的C语言 2、面向对象的C++语言 3、SDK编程 4、MFC编程
面向过程的C语言 C和C++的最主要区别是:C语言中没有类的概念,C++在C的语法基础上引入了类,所以C++和C的语法是基本相同的。 #include <stdio.h> void main() { printf("Hello DOS\n"); }
读取命令行参数 main函数可以带参数,第一个参数是int型的,表示参数个数,第二个参数是字符指针数组,放命令行参数的内容。 #include <stdio.h> int main(int argc, char *argv[ ]) { int i; for (i = 1; i < argc; i++) printf("%s\n", argv[i]); } return 0;
面向对象的C++语言 面向对象程序设计语言可以将一些变量和函数封装到类中,当变量被类封装后,称之为属性或者数据成员,当函数被类封装后,称之为方法或者成员函数。 定义好的一个类,然后定义一个类的实例,这个实例就叫做对象,在C++中可以用类定义对象.
在C++中使用类 #include <iostream.h> class person { public: int heart; char *name; int run() { heart=heart+20; return heart; } }; void main() int iRunStop; person ZhangSan; ZhangSan.name = "张三"; ZhangSan.heart = 72; cout<<"姓名:"<<ZhangSan.name <<endl; cout<<"跑步前心跳"<< ZhangSan.heart<<endl; iRunStop = ZhangSan.run(); cout<<"跑步后心跳"<<iRunStop<<endl; }
SDK编程 C库提供了许多函数,可以直接使用。如DeleteFile函数来删除一个文件。 #include <stdio.h> #include <stdio.h> #include <windows.h> int main() { DeleteFile("C:\\test.txt"); printf("删除成功\n"); return 0; }
Windows内部机制 Windows是一个“基于事件的,消息驱动的”操作系统。在Windows下执行一个程序,只要用户进行了影响窗口的动作(如改变窗口大小或移动、单击鼠标等)该动作就会触发一个相应的“事件”。 系统每次检测到一个事件时,就会给程序发送一个“消息”,从而使程序可以处理该事件。 每次检测到一个用户事件,程序就对该事件做出响应,处理完以后,再等待下一个事件的发生。
Windows应用程序、操作系统、计算机硬件之间的相互关系
关于消息及消息队列 操作系统是怎样将感知到的事件传递给应用程序的呢?这是通过消息机制(Message)来实现的。操作系统将每个事件都包装成消息的结构体MSG来传递给应用程序,MSG结构定义如下: typedef struct tagMSG { HWND hWnd; // 目标窗口句柄 UINT message; // 消息标识 WPARAM wParam; // 消息参数1(附加信息,16位) LPARAM lParam; // 消息参数2(附加信息,32位) DWORD time; // 消息发送时间 POINT pt; // 消息发送时鼠标的屏幕坐标 } MSG;
从变量类型区分变量用途 int x,y; x=30; y=30; typedef int WIDTH typedef int HEIGHT WIDTH x; HEIGHT y; //好处:我们从变量的类型上就可以知道x和y是用来表示宽度和高度。
在project中选Win32 Application 编写窗口应用程序 #include <windows.h> int WINAPI WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MessageBox (NULL, "Hello, Windows!", "HelloMsg", MB_OK) ; return 0 ; } 在project中选Win32 Application 示例1
WinMain函数 Windows程序的入口函数 int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow // window show state );
编写窗口应用程序 hInstance :是当前实例的句柄 这里表示应用程序本身; hPrevInstance:总是为NULL。 在Windows早期版本中,当多次同时运行同一个程序时,系统会创建该程序的多个“实例”,同一程序的所有实例共享代码和内存。程序需要检查hPrevInstance来判断自身的其他实例是否正在运行,以便从已经存在的实例中获取数据。在32位版本中,统一程序运行方式改变了,不再需要了!
编写窗口应用程序 lpCmdLine:是运行程序的命令行 用于接受并存放程序运行时所需的参数 nCmdShow:用于指定程序窗口最初的显示模式 可以正常显示,也可以在初始化就最大化或者最小化。
窗口的创建 创建一个完整的窗口需要经过下面四个操作步骤: 设计一个窗口类; 注册窗口类; 创建窗口; 显示及更新窗口。
1、设计窗口类 typedef struct _WNDCLASS { UINT style; //窗口样式 WNDPROC lpfnWndProc; //指向窗口过程函数 int cbClsExtra; //窗口类的附加内存空间(0) int cbWndExtra; //窗口的附加内存空间(0) HANDLE hInstance; //指向程序的实例句柄 HICON hIcon; //指向图标的句柄 HCURSOR hCursor; //指向光标的句柄 HBRUSH hbrBackground; //背景画刷句柄 LPCTSTR lpszMenuName; //菜单资源名 LPCTSTR lpszClassName; //指定窗口类的名称 } WNDCLASS;
--窗口类的类型 补充知识: 在程序中经常要用到一类变量,这个变量里的每一位(bit)都对应某一种特性。当该变量的某位为1时,表示有该位对应的那种特性,当该位为0时,即没有该位所对应的特性。当变量中的某几位同时为1时,就表示同时具有几种特性的组合。一个变量中的哪一位代表哪种意义,不容易记忆,所以我们经常根据特征的英文拼写的大写去定义一些宏,该宏所对应的数值中仅有与该特征相对应的那一位(bit)为1,其余的bit都为0。
--窗口类的类型 使用goto definition,发现CS_VREDRAW=0x0001,CS_HREDRAW=0x0002,CS_DBLCLKS =0x0008,CS_NOCLOSE=0x0200。共同点就是只有一位为1,其余位都为0。 如果希望某一变量的值既有CS_VREDRAW又有CS_HREDRAW特性,可使用或运算,如:style=CS_VREDRAW | CS_HREDRAW ; 如果希望在某一变量原有的几个特征上去掉其中一个特征,用取反(~)之后再进行与(&)运算。如在刚才的style的基础上去掉CS_NOCLOSE特征,可以用: style=style& ~CS_NOCLOSE;
--窗口过程函数 lpfnWndProc:指定了这一类型窗口的过程函数,也称回调函数。 回调函数的原理:当应用程序收到给某一窗口的消息时,就应该调用某一函数来处理这条消息。这一调用过程不用应用程序自己来实施,而由操作系统来完成,但是,回调函数本身的代码必须由应用程序自己完成。对于一条消息,操作系统到底调用应用程序中的哪个函数(回调函数)来处理呢?操作系统调用的就是接受消息的窗口所属的类型中的lpfnWndProc成员指定的函数。每一种不同类型的窗口都有自己专用的回调函数,该函数就是通过lpfnWndProc成员指定的。
--窗口过程函数 举例:汽车厂家生产汽车好比应用程序创建窗口,用户使用汽车好比操作系统管理窗口,某种汽车在销售前就指定好了修理站(类似回调函数),当用户的汽车出现故障后(类似窗口收到消息),汽车用户(类似操作系统)自己直接找到修理站去修理,不用厂家(类似应用程序)亲自将车送到修理站去修理,但修理站还得由厂家事先建造好。
--设计窗口类的示例代码 WNDCLASS wndcls; wndcls.style=CS_HREDRAW | CS_VREDRAW; wndcls.cbClsExtra=0; wndcls.cbWndExtra=0; wndcls.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH); wndcls.hCursor=LoadCursor(NULL,IDC_CROSS); wndcls.hIcon=LoadIcon(NULL,IDI_ERROR); wndcls.hInstance=hInstance; wndcls.lpfnWndProc=WindowProc; wndcls.lpszClassName="xiexin2006"; wndcls.lpszMenuName=NULL;
2、注册窗口类 ATOM RegisterClass( CONST WNDCLASS *lpWndClass); 刚才有: WNDCLASS wndcls; 所以: RegisterClass(&wndcls);
3、创建窗口 HWND CreateWindow( LPCTSTR lpClassName, // 1.pointer to registered class name LPCTSTR lpWindowName, //2. pointer to window name DWORD dwStyle, // 3.window style int x, // 4.horizontal position of window int y, //5. vertical position of window int nWidth, //6. window width int nHeight, // 7.window height HWND hWndParent, //8. handle to parent or owner window HMENU hMenu, // 9.handle to menu or child-window identifier HANDLE hInstance, // 10.handle to application instance LPVOID lpParam //11. pointer to window-creation data );
--创建窗口的示例代码 HWND hwnd; hwnd=CreateWindow("xiexin2006","My window", WS_OVERLAPPEDWINDOW, 0,0,600,400,NULL,NULL,hInstance,NULL); 注:创建成功后,返回系统分配给窗口句柄,否则返回NULL。因此要用一个句柄变量(如hwnd)来保存 WS_OVERLAPPEDWINDOW类型的窗口具有: Creates an overlapped window with the WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX, and WS_MAXIMIZEBOX styles.
4、显示及更新窗口 BOOL ShowWindow( HWND hWnd, //窗口创建成功后的句柄 int nCmdShow // 窗口显示状态 ); BOOL UpdateWindow( HWND hWnd // 窗口创建成功后的句柄 ); ShowWindow(hwnd ,SW_SHOWNORMAL); UpdateWindow(hwnd); 还有:SW_MAXIMIZE , SW_SHOWMAXIMIZED SW_SHOWMINIMIZED SW_SHOWDEFAULT 等
--创建窗口的示例代码 HWND hwnd; hwnd=CreateWindow("xiexin2006","My window", WS_OVERLAPPEDWINDOW, 0,0,600,400,NULL,NULL,hInstance,NULL); WS_OVERLAPPEDWINDOW类型的窗口具有: Creates an overlapped window with the WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX, and WS_MAXIMIZEBOX styles.
Windows消息循环 消息队列和在应用程序中的轮询处理
Windows消息循环 BOOL GetMessage( LPMSG lpMsg, // 指向消息结构体的地址 HWND hWnd, // 接收消息的窗口句柄 UINT wMsgFilterMin, // 消息的最小值(一般取0) UINT wMsgFilterMax //消息的最大值(一般取0) ); 后两个参数若都设为0,则接收所有消息; 接收到除WM_QUIT 外的消息均返回非0值; 对于WM_QUIT返回0,出现错误返回-1。
Windows消息循环 MSG msg; while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; TranslateMessage(&msg):将虚拟键消息转换为字符消息 DispatchMessage(&msg ):把消息投递到窗口过程
窗口过程函数 LRESULT CALLBACK WindowProc( HWND hwnd, // 消息的窗口句柄 UINT uMsg, // 消息代码 WPARAM wParam, // 消息的附加参数1 LPARAM lParam //消息的附加参数2 ); 该函数内部主要通过switch-case来确定窗口接收的是什么消息,以及如何处理这些消息。
窗口过程函数示例代码 LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg, WPARAM wParam,LPARAM lParam) { switch(uMsg) { case WM_LBUTTONDOWN: MessageBox(hwnd,"mouse clicked","message",0); break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd,uMsg,wParam,lParam); } return 0;
总结:SDK开发程序的步骤 1、包含相关头文件,如window.h及stdio.h等 2、写WinMain(……)入口函数 A、设计一个窗口类(WNDCLASS) B、注册窗口类(RegisterClass) C、创建窗口(CreateWindow) D、显示及更新窗口(ShowWindow UpdateWindow) E、消息循环(获取、转换、投递消息函数) 3、写窗口过程函数 用switch来接收各类消息并作相应处理 示例2
利用SDK函数创建窗口 #include <windows.h> #include <stdio.h> WNDCLASS wc; HWND h_wnd; MSG msg; /* 消息处理函数wndProc的声明*/ long WINAPI WindowProc(HWND,UINT,WPARAM,LPARAM); /* winMain 函数*/ int PASCAL WinMain(HINSTANCE h_CurInstance,HINSTANCE h_PrevInstance,LPSTR p_CmdLine,int m_Show) { wc.lpfnWndProc =WindowProc; wc.hInstance =h_CurInstance; wc.hbrBackground =(HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszClassName ="ECJTU";
利用SDK函数创建窗口 { TranslateMessage(&msg); DispatchMessage(&msg); } RegisterClass(&wc); //注册窗口类 h_wnd=CreateWindow("ECJTU","My Window", WS_OVERLAPPEDWINDOW,0,0,400,500, 0,0,h_CurInstance,0); //创建窗口 //显示窗口 ShowWindow(h_wnd,SW_SHOWMAXIMIZED); UpdateWindow(h_wnd); //更新窗口 //消息循环 while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (msg.wParam ); }
利用SDK函数创建窗口 示例3 LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg, WPARAM wParam,LPARAM lParam) { switch(uMsg) { case WM_LBUTTONDOWN: MessageBox(hwnd,"mouse clicked","message",0); break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd,uMsg,wParam,lParam); } return 0; 示例3
MFC编程 SDK的功能非常强大,需要记很多的函数,当面向对象编程成为主流的时候,微软将SDK的函数分类进行封装,这就是MFC(Microsoft Foundation Class) MFC中,函数名及其用法与SDK中基本一致。可多参考MSDN。
MFC编程基础 #include<afxwin.h> class sample:public CFrameWnd { Create(NULL,"My Window"); MessageBox("My Window","CFrame constructor"); } }; class App:public CWinApp { public: BOOL InitInstance(); BOOL ExitInstance(); }; BOOL App ::InitInstance() //InitInstance函数的定义// { MessageBox(0,"My Window","InitInstance", MB_OK|MB_ICONASTERISK);
MFC编程基础 示例4 sample *obj; obj=new sample; m_pMainWnd=obj; obj->ShowWindow(SW_SHOWMAXIMIZED); return TRUE; } BOOL App::ExitInstance() //ExitInstance函数定义 { MessageBox(0,"My Window","ExitInstance", MB_OK|MB_ICONHAND); //创建应用程序对象 App appobject; 示例4
MFC编程基础
MFC事件处理机制 示例5 #include<afxwin.h> class sample:public CFrameWnd { Create(NULL, "My Window"); } void OnLButtonDown(UINT,CPoint) { MessageBox("Left Button", "Hello",0); } DECLARE_MESSAGE_MAP() }; BEGIN_MESSAGE_MAP(sample,CFrameWnd) ON_WM_LBUTTONDOWN( ) END_MESSAGE_MAP( ) //消息映射 示例5
网络安全编程 网络安全基础编程技术主要包括: Socket编程 注册表编程 文件系统编程 定时器编程 驻留程序编程 多线程编程。
Socket编程 网络安全编程离不开网络编程,凡基于网络应用的程序都离不开Socket。 Socket的意思是套接字,是计算机与计算机之间通信的接口。它的出现,使程序员可以很方便地访问TCP/IP,从而开发各种网络应用的程序。 使用Winsock提供的API函数是最基本的网络编程技术。
基本概念 套接字:描述的是主机间的进程通信机制。提供了进程通信的入口点。 套接字用一个半相关描述: (协议,本地主机地址,本地端口) 一个套接字半相关包含本地主机地址、端口号、使用协议类型。 一个完整的套接字连接则用一个相关描述: (协议,本地主机地址,本地端口,远地主机地址,远地端口) 一个套接字相关则包含本地主机地址、本地端口号、远地主机地址、远地端口号以及使用的协议类型。
套接字(socket)的引入 随着Unix的应用推广,套接字在编写网络软件中得到了极大的普及。后来,套接字又被引进了Windows等操作系统,成为开发网络应用程序的非常有效快捷的工具。 套接字存在于通信区域中。通信区域也叫地址族,它是一个抽象的概念,主要用于将通过套接字通信的进程的共有特性综合在一起。套接字通常只与同一区域的套接字交换数据。Windows Sockets只支持一个通信区域:网际域( AF_INET),这个域被使用网际协议簇通信的进程使用。
网络字节顺序 不同的计算机存放多字节值的顺序不同,有的机器在起始地址存放低位字节(低位先存),有的机器在起始地址存放高位字节(高位先存)。 基于Intel的CPU(即PC机)采用的是低位先存。而网络中的TCP/IP协议使用16位整数和32位整数的高位先存格式。 为保证数据的正确性,在网络协议中需要指定网络字节顺序。
C/S模式在操作过程中采取的是主动请求方式。 客户机/服务器模式 在TCP/IP网络应用中,通信的两个进程间相互作用的主要模式是客户机/服务器模式(client/server),即客户向服务器提出请求,服务器接收到请求后,提供相应的服务。 客户机/服务器模式的建立基于两个原因: 1、网络中软硬件资源、运算能力和信息不均等 2、网间进程通信完全是异步的。 C/S模式在操作过程中采取的是主动请求方式。
客户机/服务器模式 首先服务器方要先启动,并根据请求提供相应的服务: ①打开一个通信通道并告知本地主机,它愿意在某一地址和端口上接收客户请求。 ②等待客户请求到达该端口。 ③接收到重复服务请求,处理该请求并发送应答信号。接收到并发服务请求,要激活一个新的进程(或线程)来处理这个客户请求。新进程(或线程) 处理此客户请求,并不需要对其它请求作出应答。服务完成后,关闭此新进程与客户的通信链路,并终止。 ④返回第二步,等待另一客户请求。 ⑤关闭服务器。
客户机/服务器模式 客户方: ①打开一个通信通道,并连接到服务器所在主机的特定端口。 ②向服务器发服务请求报文,等待并接收应答;继续提出请求。 ③请求结束后关闭通信通道并终止。
Windows Sockets的实现 Windows Sockets是 Windows的网络程序设计接口,它是从Berkeley Sockets扩展而来的,以动态链接库的形式提供。在继承了Berkeley Sockets主要特征的基础上,又对它进行了重要扩充,如一些异步函数及符合Windows消息驱动特性的网络事件异步选择机制。 Windows Sockets 1.1和Berkeley Sockets都是基于TCP/IP协议的; Sockets 2从Sockets 1.1发展而来,与协议无关并向下兼容,可以使用任何底层传输协议提供的通信能力,来为上层应用程序完成网络数据通讯,而不关心底层网络链路的通讯情况,真正实现了底层网络通讯对应用程序的透明。
套接字的类型 1、流式套接字(SOCK_STREAM) 提供面向连接、可靠的数据传输服务,数据无差错、无重复的发送,且按发送顺序接收。 2、数据报式套接字(SOCK_DGRAM) 提供无连接服务。数据包以独立包形式发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。 3、原始套接字(SOCK_RAW)