WINDOWS程序内部运行原理

时间:2022-12-23 21:32:56

API(Application programming interface) 应用程序编程的接口

MSG(message)  消息结构体   

操作系统将每一个事件包装成一个称为消息MSG的结构体传递给应用程序

MSG的结构定义如下:(windows user interface : platform sdk )

Typedef struct tagMSG{

              HWND hwnd;  窗口的句柄  句柄为资源的标识,按类型分为HICON /HCURSOR /HWND /HINSTANCE

              UINT message;      无符号整形具体的消息; 用宏来表示数值WM开头的为宏WM_*

              WPARAM wParam;  整型. 关于消息的附加信息. WM_CHAR消息字符的代码ASCII;

              LPARAM lParam;   ;整型. 关于消息的附加信息. PARAM=PARAMETER参数,参量

              DWORD time;      ;WORD16位的整数DWORD=Double WORD32位整数.指定消息的投递时间.

              POINT pt;          当消息被投递的时候光标太屏幕上的坐标;

}MSG,*PMSG;

POINT为一个结构体定义了点x坐标y坐标

Typedef struct tagPOINT{

              LONG x;

              LONG y;

} POINT,*PPOINT;                                                    结束时间2009年2月19日4:57:49

2009年2月20日13:18:04(第二次)

消息:消息本身,特定的消息响应

WINMAN函数

入口点函数类似于C的MAN函数

int WINAPI WinMan(

              HINSTANCE hInstance,      //handle to cerrent instance   实例(资源)的句柄,当前运行的句柄;

              HINSTANCE hPrevinstance,  //handle to previous instance  先前实例的句柄,可能为空,兄弟的实例句柄

win32下这个实例总是为空(win98、2000等);

              LPSTR lpCndLine,          //command line  LP->LONG POINTER长指针,ETRING字符串,指向字符

串的指针;类似于CHAR *   命令行参数

              int nCmdSbow              //show state  显示的状态,比如窗口的大小全屏

)   //这个分号在具体的编程过程中是不要的,需要删除掉。

DOS下的MAIN函数可以接收2个参数:ARJc、ARJv. ARJc用来接受、存放命令行参数的个数,ARJv指针数组用来存放命令行参数,同样的在windows程序当中他也可以接收命令行的参数.

WINAPI :Calling convention for system functions.

2009年2月24日15:10:15(第三次)

窗口的创建。创建一个完整的窗口需要经过下面四个操作步骤:

l         设计一个窗口类;

l         注册窗口类;

l         创建窗口;

l         显示及更新窗口;

1、设计窗口。设计一个窗口就是设计一个窗口类,他是一个结构体

WNDCLASS

Typedef struct _WNDCLASS{     //MSDN--WNDCLASS

UINIT style;   style //(Class Styles=CS)指定一个类的类型,这里是窗口类的类型;OR(|),与(&),取反(~),

                       赋值:wndclass.style=CS_HREDRAW | CS_VREDRAW (水平和垂直重画)

WNDPROC lpfnWndProc; 窗口过程。Lp=long pointer.fn=functionProc= procedure用来接收一个函数指针。

回掉函数(lpfnWndProc: Pointer to the window procedure. You must use the

CallWindowProc[回调window进程] function to call the window procedure. For more

information, see WindowProc.)。这里我们会把一个窗口名赋给他(函数名相当于一个

函数的指针),即在设计的时候就确定了一个回调函数,这里给他赋了WinSunProc

这个函数,当然WinSunProc函数具体内容是什么需要程序员自己去编写。

int  cbClsExtra;   windows程序会为每一个类管理一个内部数据结构。类附加内存,通常情况下设为零.(=0

                         类额外的一个数据。(附加字节数)

int  cbWndExtra;  windows程序会为每一个窗口管理一个内部数据结构。窗口附加内存,通常情况下也设

置为零。以上两个分配额外的内存,通常我们不需要所以设置为零。(=0)

窗口类额外的一个数据。通常赋值为零表示我们不需要额外的内存,但是如果我们需要则额外的内存空间会自己变为零。这里设置为零就是=0,不同于其他的设置为NULL,因为这里是整数。

HANDLE  hInstance;   代表我们当前程序的实例号。操作系统分配,直接通过WinMain的形参传递过来。

代表了我们当前应用程序的实例号,我们在设计窗口类的时候需要知道他是代表那

个应用程序实例的。从bInstance获得这个程序。

HICON   hIcon;   图标的句柄。用LoadIcon这个函数来赋值 hIcon=LoadIcon(……)

HCURSOR  hCursor; 光标句柄。用LoadCursor这个函数来赋值hCursor=LoadCursor(……)

HBRUSH  hbrBackground;  画刷的句柄,用的是GetStockObject()这么一个函数

LPCTSTR lpszMenuName;  LP=long point(32位),CT=constant常量,STR=string.用来设定菜单的名字

可以直接设置为空NULL,如:lpszMenuNam=NULL;

 (如果这个类型不认识可以用data type 这个来查看不是SDK)

LPCTSTR lpszClassName;  设置一个类的名字(注册窗口的时候会用到),窗口设计完成之后需要给窗口取一

个名字。

}WNDCLASS,*PWNDCLASS;

这个函数是系统定义好了格式,我们只需要拿过来用,直接对参数进行赋值。

WindowProc 回调函数,窗口过程函数

LRESULT CALLBACK WindowProc(   //CALLBACK:窗口过程函数前边定义的时候加了一个CALLBACK

                                    #define CALLBACK  _stdcall

                                    CALLBACK就是一个类型的定义

                                    LRESULT就是一个长整型,返回一个结果码。

  HWND hwnd,      // handle to window

  UINT uMsg,      // message identifier

  WPARAM wParam,  // first message parameter

  LPARAM lParam   // second message parameter

);

DefWindowProc回调函数,窗口过程函数

LRESULT DefWindowProc(

  HWND hWnd,      // handle to window

  UINT Msg,       // message identifier

  WPARAM wParam,  // first message parameter

  LPARAM lParam   // second message parameter

);

_stdcall和_cdecl这是两种调用的约定,_stdcall标准调用约定,_cdecl为c语言的调用约定。标准调用约定也是PASCAL调用约定,dephi就是pascal调用约定。_stdcall和_cdecl在参数的传递顺序和堆栈的清除这两个方面有差异。Vc++默认的调用约定选项是_cdecl.需要时要经行定义。也可以通过Project -> Project Settings ->C/C++ ->Calling convention:中选择_stdcall这种方式来设定。

LoadIcon

HICON LoadIcon(

    HINSTANCE hInstance, // handle to application instance 应用程序实例的一个句柄。当标准的图标加载的
时候这个参数用空(NULL),

LPCTSTR lpIconName  // name string or resource identifier当第一个参数取值为NULL时第二个参数可以选择

);

如:LoadIcon(NULL,IDI_ERROR)     // ID=identifier

LoadCursor

HCURSOR LoadCursor(

HINSTANCE hInstance,  // handle to application instance 应用程序实例的一个句柄,如果想用一个标准的光标

这个参数需要设置为NULL

LPCTSTR lpCursorName  // name or resource identifier  同样当前一个参数设置为NULL是这个可以选择微

软提供的光标函数。

);

GetStockObject()  获取一个笔、画刷、调色板、的句柄。

HGDIOBJ GetStockObject(

  int fnObject   // stock object type

);

*=(HBRUSH) GetStockObject(WHITE_BRUSH);   // (HBRUSH) 这里是一个强制转换。因为GetStockObject返回的是一个GetStockObject的类型,而hbrBackground定义的为HBRUSH的类型,C++语言是一个强类型语言,对函数的类型要求的很严。强制类型转换必须要有可比性,即两个类型可以转换,这种转换在编译的时候不会报错但是在执行的时候会出现错误。

2、注册。窗口设置完之后需要对其进行注册(向操作系统注册),RegisterClass()窗口的注册函数

RegisterClass

ATOM RegisterClass(

  CONST WNDCLASS *lpWndClass  // class data 窗口类结构体的指针直接用定义的窗口类结构体的变量加上取

地址符&

);

3、创建一个窗口

首先需要定义一个句柄(HWND),利用这个句柄的变量保存我们新创建的窗口的标识。然后利用CreateWindow函数来进行窗口的创建。

HWND hwnd;

Hwnd=CreateWindow(……);

CreateWindow

HWND CreateWindow(

  LPCTSTR lpClassName,  // registered class name 注册的类名,设计窗口类的时候指定的名字,如果名称与开始

定义的不一样那么WinMain程序会运行但是窗口是不回产生的。创建窗口的时候一

定要基于一个已经注册之后的窗口类名来设定

  LPCTSTR lpWindowName, // window name  窗口的名字,即我们产生这个窗口的时候他的标题。在标题栏上显

示的窗口的名字,标题栏是一条蓝色的在窗口顶端的长条

  DWORD dwStyle,        // window style  窗口的类型,要和刚才的类的类型区分开来。(WS)如:

WS_OVERLAPPEDWINDOW

  int x,                // horizontal position of window 窗口的水平坐标; CW_USEDEFAULT,如果x设置为

CW_USEDEFAULT(系统缺省坐标)那么y坐标值就会被忽略。另外屏幕坐标左上角为

原点(0,0)而数学中的为左下角是原点

  int y,                // vertical position of window  窗口的垂直坐标; x,y显示的时候窗口的左上角的x,y坐标.

  int nWidth,           // window width 窗口的宽度,CW_USEDEFAULT用的是系统缺省的宽度和高度。同样

如果用CW_USEDEFAULT则nHeight会被忽略。

  int nHeight,          // window height 窗口的高度

  HWND hWndParent,   // handle to parent or owner window 窗口的句柄,这里指的是父窗口的句柄,如果程

序只有1个窗口没有父窗口则可以设置为空(NULL)

  HMENU hMenu,      // menu handle or child identifier  菜单的句柄。如果用不倒菜单那么我们可以把菜单句柄

设置为空(NULL);

  HINSTANCE hInstance,  // handle to application instance 当前应用程序实例的句柄,通过WinMain函数传递过

来,直接把实例号hInstance,复制过来就可以了

  LPVOID lpParam      // window-creation data  当窗口创建的时候都会有WM_CREATE这个消息产生,

作为WM_CREATE的附加消息有两个由lParam的消息内容传进来。指向

CREATESTRUCT结构体,该结构题的lParam参数是通过创建窗口时产生的

WM_CREATE消息时产生的附加消息中的lParam参数传递过来的,即:作为

WM_CREATE附加消息lParam传递过来的参数数据的指针。如果一个应有实例

CreateWindow创建一个多文档multiple document interface (MDI)界面的窗口该函数

lpParam指针指向结构体CLIENTCREATESTRUCT

);

 

WS_OVERLAPPEDWINDOW

WS_OVERLAPPEDWINDOW(

WS_OVERLAPPED      |    //标识产生一个层叠的窗口,层叠的窗口就是有一个标题栏还有一个边框

WS_CAPTION          |    //创建一个有标题栏的窗口

WS_SYSMENU         |    //创建一个带有系统菜单的窗口

WS_THICKFRAME      |    //创建一个具有可调边框的窗口

WS_MINIMIZEBOX     |     //创建一个具有最小化按钮的窗口

WS_MAXIMIZEBOX         //创建一个具有最大化按钮的窗口

). Same as the WS_TILEDWINDOW style. 

上边的特征都是有二进制位表示有无。

CREATESTRUCT

typedef struct tagCREATESTRUCT {

    LPVOID    lpCreateParams;

    HINSTANCE hInstance;

    HMENU     hMenu;

    HWND      hwndParent;

    int       cy;

    int       cx;

    int       y;

    int       x;

    LONG      style;

    LPCTSTR   lpszName;

    LPCTSTR   lpszClass;

    DWORD     dwExStyle;

} CREATESTRUCT, *LPCREATESTRUCT;

WM_CREATE    WindowProc

LRESULT CALLBACK WindowProc(

  HWND hwnd,       // handle to window

  UINT uMsg,       // WM_CREATE

  WPARAM wParam,   // not used

  LPARAM lParam    // creation data (LPCREATESTRUCT)

);

4、显示、更新窗口

窗口创建完之后需要显示出来。显示窗口调用函数为:ShowWindow

ShowWindow  指定窗口的显示状态

BOOL ShowWindow(

  HWND hWnd,     // handle to window 窗口的句柄,即要显示的是哪一个窗口,把窗口的标识赋给他

  int nCmdShow   // show state 窗口的显示状态。如:最大化显示,最小化显示等。(SHOW_SHOWNORMAL

正常化显示)

);

UpdateWindow

显示完窗口之后我们还需要调用一个函数,即为更新窗口的函数,UpdateWindow。当然这个函数如果不添加也不影响窗口的产生。

BOOL UpdateWindow(

  HWND hWnd   // handle to window  窗口句柄。

);

消息循环,这里最重要的环节。

首先用MSG结构体定义了一个消息结构体的变量。用While循环对GetMessage进行循环。GetMessage调用消息队列中的消息。

MSG msg;

While(GetMessage(&msg,NULL,0,0)) //GetMessage取到的消息不是WM_QUIT时执行While循环。

{

     TranslateMessage(&msg);    // 转换消息,翻译消息。对取到的消息对进行转换。当我们按下某一个按键

的时候系统将会产生一个WM_KEYDOWN和WM_KEYUP这样两个的消

息,并提供一个按键的虚拟的扫描码。TranslateMessage将WM_KEYDOWN

和WM_KEYUP消息对转换成ASCII码WM_CHAR消息,并将转换后的消息

投递到消息队列中,转换过程中不会影响原来的消息只会产生一个新的消

息。如果不用这个函数那么我们不可能收到WM_CHAR消息的。

     DispatchMessage(&msg);    // 将收到的消息(TranslateMessage(&msg)转换的消息)传递到窗口的回调函数

即窗口的过程函数中处理,即将消息路由给了操作系统,然后操作系统去调

用窗口过程函数,即我们在设计窗口类WNDCLASS的时候设计的窗口过程

函数WNDPROC lpfnWndProc 所设定的回调函数进行处理。

}

GetMessage

BOOL GetMessage(    // 如果返回值是WM_QUIT则返回一个零值,如果返回的不是WM_QUIT则返回的是一

个非零值。

LPMSG lpMsg,         // message information   [out] Pointer to an MSG structure that receives message

information from the thread's(线程) message queue. 消

息结构体的指针。

  HWND hWnd,           // handle to window   句柄表示想要获取那个窗口的消息,如果设置为NULL,标识

我们要获取属于这个调用线程的任何窗口的消息

  UINT wMsgFilterMin,  // first message         指定消息的最小的消息值

  UINT wMsgFilterMax   // last message         指定一个消息的最大的消息值,即最后一个消息(MSDN有一

个错误他将last写成first)如果这两个都设置为零那么

GetMessage就没有对消息进行过滤而是针对所有的消息,没有

范围的过滤。

);

[out]->表明我在对lpMsg传参的时候不要对内部成员经行初始化,只需要定义一个结构体的变量将他的地址放在哪里,通过函数的调用他会自动的填充消息结构体内部的成员变量。

GetMessag返回的是一个BOOL型的值。当从消息队列中取回消息是返回值为真,当消息队列中始终都有的消息的时候则返回的值始终都是真。

TranslateMessage

BOOL TranslateMessage(

  CONST MSG *lpMsg   // message information

);

DispatchMessage

LRESULT DispatchMessage(

  CONST MSG *lpmsg   // message information

);

窗口过程函数(WNDPROC lpfnWndProc指定的函数),即回调函数里边的代码

这里设定的窗口调用函数的名称为WinSunProc.怎么知道WinSunProc是什么样的格式呢?选择查看的方式如下:

 WNDCLASS -> Platform SDK: Windows User Interface -> 选择see WindowProc 中的WindowProc ->我们可以看到函数的形式规定的形式(函数名称可以更改,但是参数的类型不能更改但参数的名字可以更改):

LRESULT CALLBACK WindowProc(     // LRESULT就是一个LONG型 CALLBACK后边将介绍

  HWND hwnd,      // handle to window   窗口的句柄

  UINT uMsg,      // message identifier    消息的标识

  WPARAM wParam,  // first message parameter     消息的附加参数

  LPARAM lParam   // second message parameter    消息的第二个附加参数

);

实际上是将消息结构体的前四个参数作为参数传递给WindowProc(这个名称用户可以自己更改)

sprintf: printf在c语言中是想屏幕中输出内容,sprintf是c语言中的一个库函数,是格式化一个文本到内存buffer中,即一个内存区中.指定的区域中打印一些数据的。如下:

char szChar[20];

sprintf(szChar,”char is %d”,wParam);   //将wParam的ASCII码的消息以%d的格式格式化到szChar这个字符数组

当中。

MessageBox(hwnd,szChar,”weixin”,MB_ok);   // MessageBox用来弹出一个消息框.

MessageBox

int MessageBox(

  HWND hWnd,       // handle to owner window   消息框被创建那个窗口拥有这个消息框的句柄

  LPCTSTR lpText,     // text in message box       消息显示的文本

  LPCTSTR lpCaption,  // message box title         消息的标题,即在消息的蓝框中的内容显示

  UINT uType          // message box style        消息框的类型,如MB_OK表示消息框包含了一个OK按键

的button按钮。MB_OK定义的是”0”,我们可以直接用”0”代

MB_OK .

);

文字的输出

HDC hDC;       // HDC是一个句柄,DC(device context设备上下文)的句柄。DC是系统内部维护的数据结构占

内存。

hDC=GetDC(hwnd); //获取函数hDC的句柄

TextOut(hDC,0,50,” 计算机编程语言培训”,strlen(“计算机编程语言培训”)); //文本输出的函数,在窗口上输出一个

文本。

ReleaseDC(hwnd,hDC);   //释放DC.如果我们使用了一个DC而没有在使用完之后释放DC那么会造成内存的泄

露。   GetDCReleaseDC是一对

HDC

Handle to a device context (DC).

GetDC

HDC GetDC(

  HWND hWnd   // handle to window 窗口的句柄。跟那个窗口相关的DC,利用DC画图的时候就画在那个窗口

上。

);

TextOut

BOOL TextOut(

  HDC hdc,           // handle to DC   DC的句柄

  int nXStart,       // x-coordinate of starting position  开始x坐标位置

  int nYStart,       // y-coordinate of starting position   开始y坐标位置

  LPCTSTR lpString,  // character string   输出文本内容

  int cbString       // number of characters  输出文本字符数量。 用c的strlen.

);

WM_PAINT  当窗口重绘的时候就会发送这个消息。当窗口从无到有的时候会使用这个函数,同时在我们在调试运行时设置的断点中不能在WM_PAINT中设置,如果这样做了那么窗口将会永远不会产生。前面说过当我们窗口的水平或者垂直坐标发生改变的时候窗口都会重绘,重绘就是通过发送WM_PAINT这个消息来完成的。

Case WM_PAINT: //窗口的绘画,下边为响应消息的设置

hdc=BeginPaint(hwnd,&ps); // 获取DC的句柄,这个DC是在选择语句上边预定的。

TextOut(hdc,0,0,” 北京维新科学技术培训中心”,strlen(”北京维新科学技术培训中心”));  //

EndPaint(hwnd,&ps);  //释放DC.BeginPaint和EndPaint是一对,并且只能在WM_PAINT上使用。 

break;

BeginPaint

HDC BeginPaint(

  HWND hwnd,            // handle to window   窗口的句柄

  LPPAINTSTRUCT lpPaint // paint information  设置PAINTSTRUCT结构体的指针。

);

BeginPaint准备为指定的窗口进行绘画,BeginPaint函数自动填充PAINTSTRUCT结构体,PAINTSTRUCT结构体不需要我们来维护由系统内部lpPaint来维护

PAINTSTRUCT

typedef struct tagPAINTSTRUCT {

  HDC  hdc;

  BOOL fErase;

  RECT rcPaint;

  BOOL fRestore;

  BOOL fIncUpdate;

  BYTE rgbReserved[32];

} PAINTSTRUCT, *PPAINTSTRUCT;

WM_CLOSE

case WM_CLOSE: //当我们点击关闭按钮的时候会产生这样的消息。

if(IDYES= = MessageBox(hwnd,”你是否要退出程序?”,”weixin”,MB_YESNO)) //由MessageBox返回一个ID*

的参数,判断该返回参数与IDYES的比较情况。

{

    DestroyWindow(hwnd); //  销毁窗口

}

break;

IDYES是一个常量,我们在程序设计的是有有一个小经验:在if语句判断的时候一般把常量放在前边。这样做方便我们找到错误。如:if(x=1)if(1=x),这两种是在我们输入的时候容易出错的两种情况,第二种在编译的时候就会报错,而第一种x=1是“真”,编译的时候不会报错。

DestroyWindow

BOOL DestroyWindow(

  HWND hWnd   // handle to window to destroy  //要被销毁的窗口的句柄。

);

DestroyWindow 这个函数会发送WM_DESTROY 和WM_NCDESTROY这两个消息到消息队列中。

WM_DESTROY

Case WM_DESTROY

     PostQuitMessage(0);

     Break;

小经验:

case WM_CLOSE:

if(IDYES= = MessageBox(hwnd,”你是否要退出程序?”,”weixin”,MB_YESNO))

{

    DestroyWindow(hwnd);

}

break;

Case WM_DESTROY

     PostQuitMessage(0);

     Break;

这个程序片段的意思是当MessageBox弹出后用户选择YES时,if语句为真,执行DestroyWindow(hwnd)关掉窗口,同时产生WM_SESTROY和WM_NCDESTROY两个消息,在case WM_DESTROY中将关掉程序执行的进程,同时程序结束。

case WM_CLOSE:

DestroyWindow(hwnd);

break;

Case WM_DESTROY

     if(IDYES= = MessageBox(hwnd,”你是否要退出程序?”,”weixin”,MB_YESNO))

     {

 PostQuitMessage(0);

     }

     Break;

这个程序片段与上边的略有不同,但是执行结果则是完全不一样的。当执行WM_CLOSE时程序会首先执行DestroyWindow(hwnd)关掉窗口产生WM_SESTROYWM_NCDESTROY两个消息到消息对立中,这个时候响应消息WM_DESTROY,开始执行if的判断,此时才弹出MessageBox的消息对话框,如果用户选择的是否,那么消息对话框关闭了,if语句为否不执行其中的PostQuitMessage(0)也就无法产生WM_QUIT消息,此时窗口关闭了,但是程序没有终止而是在后台中运行。

PostQuitMessage

VOID PostQuitMessage(       //产生WM_QUIT这个消息

  int nExitCode              // exit code

);

default

在写消息循环的时候default语句是必不可少的,是对缺省的没有定义的消息由系统进行默认的响应,如果没有那么那些没有定义的消息将会缺少归宿,并且窗口可能不能正常的显示,但是程序还是在后台运行。

deault:

     return DefWindowProc(hwnd,uMsg,wParam,lParem);

return 0;

函数的正常返回值,返回一个”0”值。

编写一个程序:

新建一个projectFile ->New…->Projects ->Win32 Application |Location:存放地址|Project name:工程名字 ->OK ->step 1:An empt project -> finish ->ok .

新建:File ->New…->Files ->c++ source file(c++源文件)|file:同样的名字 ->ok.

既然我们编写的是windows程序,所以就要包含#include <windows.h>  windows头文件 ,还有一个就是c语言的头文件#include <stdio.h> 原因是我们要用到c语言的库函数。

--------------------------------------------------------------------------------------------------------------------------------------

#include <windows.h>

#include <stdio.h>

 

LRESULT CALLBACK WinSunProc(    //可以通过MSDN查找,不过函数的名称要根据自己的需要修改。

  HWND hwnd,      // handle to window

  UINT uMsg,      // message identifier

  WPARAM wParam,  // first message parameter

  LPARAM lParam   // second message parameter

);                   //函数原型声明

 

int WINAPI WinMain(

  HINSTANCE hInstance,      // handle to current instance

  HINSTANCE hPrevInstance,  // handle to previous instance

  LPSTR lpCmdLine,          // command line

  int nCmdShow              // show state

  )

{

       WNDCLASS wndcls;

       wndcls.cbClsExtra=0;

       wndcls.cbWndExtra=0;

       wndcls.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);

       wndcls.hCursor=LoadCursor(NULL,IDC_CROSS);

       wndcls.hIcon=LoadIcon(NULL,IDI_QUESTION);

       wndcls.hInstance=hInstance;

       wndcls.lpfnWndProc=WinSunProc;

       wndcls.lpszClassName="liqiang2009";

       wndcls.lpszMenuName=NULL;

       wndcls.style=CS_HREDRAW|CS_VREDRAW;

       RegisterClass(&wndcls);

 

       HWND hwnd;

       hwnd=CreateWindow("liqiang2009","一个简单的窗口",WS_OVERLAPPEDWINDOW,

              CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,

              NULL,NULL,hInstance,NULL);

 

       ShowWindow(hwnd,SW_SHOWNORMAL);

       UpdateWindow(hwnd);

 

       MSG msg;

       while(GetMessage(&msg,NULL,0,0))

       {

              TranslateMessage(&msg);

              DispatchMessage(&msg);

       }

       return 0;

}

 

LRESULT CALLBACK WinSunProc(

  HWND hwnd,      // handle to window

  UINT uMsg,      // message identifier

  WPARAM wParam,  // first message parameter

  LPARAM lParam   // second message parameter

)

{

       switch(uMsg)

       {

       case WM_CHAR:

              char szChar[20];

              sprintf(szChar,"您所输入的按键值为:%d%c",wParam,wParam);

              MessageBox(hwnd,szChar,"按键值",0);

              break;

       case WM_LBUTTONDOWN:

              MessageBox(hwnd,"您按下了鼠标的左键","按键值",0);

              HDC hdc;

              hdc=GetDC(hwnd);

              TextOut(hdc,0,25,"HDC hdc",strlen("HDC hdc"));

              ReleaseDC(hwnd,hdc);

              break;

       case WM_PAINT:

              HDC hDC;

              PAINTSTRUCT ps;

              hDC=BeginPaint(hwnd,&ps);

              TextOut(hDC,0,0,"重刷的时候不会被去掉的文字",strlen("重刷的时候不会被去掉的文字"));

              EndPaint(hwnd,&ps);

              break;

       case WM_CLOSE:

              if(IDYES==MessageBox(hwnd,"是否真的退出?","询问窗口",MB_YESNO))

              {

                     DestroyWindow(hwnd);

              }

              break;

       case WM_DESTROY:

              PostQuitMessage(0);

              break;

       default:

              return DefWindowProc(hwnd,uMsg,wParam,lParam);

       }

       return 0;

}

------------------------------------------------------------------------------------------------------------------------------------------

Build Log

--------------------Configuration: WinMain - Win32 Debug--------------------

Command Lines

Creating temporary file "C:/DOCUME~1/Li/LOCALS~1/Temp/RSPFE.tmp" with contents

[

/nologo /MLd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Fp"Debug/WinMain.pch" /YX /Fo"Debug/" /Fd"Debug/" /FD /GZ /c

"D:/soft-教程/vc++/孙鑫课件/lessons/lesson1/WinMain/WinMain.cpp"

]

Creating command line "cl.exe @C:/DOCUME~1/Li/LOCALS~1/Temp/RSPFE.tmp"

Creating temporary file "C:/DOCUME~1/Li/LOCALS~1/Temp/RSPFF.tmp" with contents

[

kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /incremental:yes /pdb:"Debug/WinMain.pdb" /debug /machine:I386 /out:"Debug/WinMain.exe" /pdbtype:sept

"./Debug/WinMain.obj"

]

Creating command line "link.exe @C:/DOCUME~1/Li/LOCALS~1/Temp/RSPFF.tmp"

Output Window

Compiling...

WinMain.cpp

Linking...

Results

WinMain.exe - 0 error(s), 0 warning(s)

 

编写程序后小结:

第一个小结:

       case WM_CLOSE:

              if(IDYES==MessageBox(hwnd,"是否真的退出?","询问窗口",MB_YESNO))

              {

                     DestroyWindow(hwnd);

              }

              break;

       case WM_DESTROY:

              PostQuitMessage(0);

              break;

可以直接关闭而不弹窗修改为:

       case WM_CLOSE:

              DestroyWindow(hwnd);

              break;

       case WM_DESTROY:

              PostQuitMessage(0);

              break;

第二个小结:

       case WM_PAINT:

              HDC hDC;

              PAINTSTRUCT ps;

              hDC=BeginPaint(hwnd,&ps);

              TextOut(hDC,0,0,"重刷的时候不会被去掉的文字",strlen("重刷的时候不会被去掉的文字"));

              TextOut(hDC,0,25,szChar,strlen(szChar));

              EndPaint(hwnd,&ps);

              break;

TextOut可以用多次,多次使用时只要不是文字的覆盖那么每次TextOut的执行不会清除上一次的结果。

第三个小结:

变量可以设置为全局的但是程序不能设置为全局的,如:

#include <windows.h>

#include <stdio.h>

       char szChar[26];

……

第四个小结:

函数调用时,被调用的函数需要提前做定义,同时使用的时候要和定义的函数名称一致,否则程序无法执行。

第五个小结:

当程序修改后调试的时候必须将以前调试运行的程序关闭,否则会出现错误:

--------------------Configuration: WinMain - Win32 Debug--------------------

Linking...

LINK : fatal error LNK1168: cannot open Debug/WinMain.exe for writing

Error executing link.exe.

 

WinMain.exe - 1 error(s), 0 warning(s)

第六个小结:

/0,/n etc.在sprintf();中不起任何作用。

第七个小结:

Sprintf中可以有多个参数的变量,如同时有%d、%c etc.

sprintf(szChar,"您所输入的值为:/n %d,%c   ",wParam,wParam);

遇到一个问题:

当关掉编译器后双击*.cpp这个文件的时候会打开编译器,但是编译运行的时候系统就会报错!!!

Build Log

--------------------Configuration: WinMain - Win32 Debug--------------------

Command Lines

Creating temporary file "C:/DOCUME~1/Li/LOCALS~1/Temp/RSP13C.tmp" with contents
[
kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:yes /pdb:"Debug/WinMain.pdb" /debug /machine:I386 /out:"Debug/WinMain.exe" /pdbtype:sept 
"./Debug/WinMain.obj"
]
Creating command line "link.exe @C:/DOCUME~1/Li/LOCALS~1/Temp/RSP13C.tmp"

Output Window

 
Linking...
LIBCD.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
Debug/WinMain.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.

Results

WinMain.exe - 2 error(s), 0 warning(s)

自己修改的程序:

#include <windows.h>

#include <stdio.h>

       char szChar[26];

 

LRESULT CALLBACK WinSunProc(    //可以通过MSDN查找,不过函数的名称要根据自己的需要修改。

  HWND hwnd,      // handle to window

  UINT uMsg,      // message identifier

  WPARAM wParam,  // first message parameter

  LPARAM lParam   // second message parameter

);                   //函数原型声明

 

int WINAPI WinMain(

  HINSTANCE hInstance,      // handle to current instance

  HINSTANCE hPrevInstance,  // handle to previous instance

  LPSTR lpCmdLine,          // command line

  int nCmdShow              // show state

  )

{

       sprintf(szChar,"你好");  //要想对szChar屏幕输出的时候无乱码,sprintf要放在主函数里对scChar定义。

 

       WNDCLASS wndcls;

       wndcls.cbClsExtra=0;

       wndcls.cbWndExtra=0;

       wndcls.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);

       wndcls.hCursor=LoadCursor(NULL,IDC_CROSS);

       wndcls.hIcon=LoadIcon(NULL,IDI_QUESTION);

       wndcls.hInstance=hInstance;

       wndcls.lpfnWndProc=WinSunProc; 

       wndcls.lpszClassName="liqiang2009";

       wndcls.lpszMenuName=NULL;

       wndcls.style=CS_HREDRAW|CS_VREDRAW;

       RegisterClass(&wndcls);

 

       HWND hwnd;

       hwnd=CreateWindow("liqiang2009","一个简单的窗口",WS_OVERLAPPEDWINDOW,

              CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,

              NULL,NULL,hInstance,NULL);

 

       ShowWindow(hwnd,SW_SHOWNORMAL);

       UpdateWindow(hwnd);

 

             

 

       MSG msg;

       while(GetMessage(&msg,NULL,0,0))

       {

              TranslateMessage(&msg);

              DispatchMessage(&msg);

       }

       return 0;

}

 

LRESULT CALLBACK WinSunProc(

  HWND hwnd,      // handle to window

  UINT uMsg,      // message identifier

  WPARAM wParam,  // first message parameter

  LPARAM lParam   // second message parameter

)

{

//     char szChar[22];

//     sprintf(szChar," 你好");

       switch(uMsg)

       {

       case WM_CHAR:

              HDC hdcc;

              hdcc=GetDC(hwnd);

              sprintf(szChar,"您所输入的值为:/n %d,%c   ",wParam,wParam);

              TextOut(hdcc,0,75,szChar,strlen(szChar));

              MessageBox(hwnd,szChar,"按键值",0);

              ReleaseDC(hwnd,hdcc);

              break;

       case WM_LBUTTONDOWN:

              MessageBox(hwnd,"您按下了鼠标的左键","按键值",0);

              HDC hdc;

              hdc=GetDC(hwnd);

              TextOut(hdc,0,50,"HDC hdc",strlen("HDC hdc"));

              ReleaseDC(hwnd,hdc);

              break;

       case WM_PAINT:

              HDC hDC;

              PAINTSTRUCT ps;

              hDC=BeginPaint(hwnd,&ps);

              TextOut(hDC,0,0,"重刷的时候不会被去掉的文字",strlen("重刷的时候不会被去掉的文字"));

              TextOut(hDC,0,25,szChar,strlen(szChar));

              EndPaint(hwnd,&ps);

              break;

       case WM_CLOSE:

//            if(IDYES==MessageBox(hwnd,"是否真的退出?","询问窗口",MB_YESNO))

//            {

                     DestroyWindow(hwnd);

//            }

              break;

       case WM_DESTROY:

              PostQuitMessage(0);

              break;

       default:

              return DefWindowProc(hwnd,uMsg,wParam,lParam);

       }

       return 0;

}