• VC++学习(1):Windows程序内部运行原理


    Windows应用程序,操作系统,计算机硬件之间的相互关系

              

    关于API:向下的箭头表示应用程序可以通知操作系统执行某个具体的动作,如操作系统能够控制声卡发出声音,但它并不知道应该何时发出何种声音,需要应用程序告诉操作系统该发出什么样的声音。这个关系好比有个机器人能够完成行走的功能,但是,如果人们不告诉它往哪个方向上走,机器人是不会主动行走的。这里的机器人就是操作系统,人们就是应用程序。

      那么,应用程序是如何通知操作系统执行某个功能的呢?有过编程经验的读者都应该知道,在应用程序中要完成某个功能,都是以函数调用的形式实现的,同样,应用程序也是以函数调用的方式来通知操作系统执行相应的功能的。操作系统所能够完成的每一个特殊功能通常都有一个函数与其对应,也就是说,操作系统把它所能够完成的功能以函数的形式提供给应用程序使用,应用程序对这些函数的调用就叫做系统调用,这些函数的集合就是Windows操作系统提供给应用程序编程的接口(Application Programming Interface),简称Windows API。如CreateWindow就是一个API函数,应用程序中调用这个函数,操作系统就会按照该函数提供的参数信息产生一个相应的窗口。

    关于消息和消息队列:向上的箭头表示操作系统能够将输入设备的变化上传给应用程序。如用户在某个程序活动时按了一下键盘,操作系统马上能够感知到这一事件,并且能够知道用户按下的是哪一个键,操作系统并不决定对这一事件如何作出反应,而是将这一事件转交给应用程序,由应用程序决定如何对这一事件作出反应。好比有个蚊子叮了我们一口,我们的神经末梢(相当于操作系统)马上感知到这一事件,并传递给了我们的大脑(相当于应用程序),我们的大脑最终决定如何对这一事件作出反应,如将蚊子赶走,或是将蚊子拍死。对事件作出反应的过程就是消息响应。

      操作系统是怎样将感知到的事件传递给应用程序的呢?这是通过消息机制(Message)来实现的。操作系统将每个事件都包装成一个称为消息的结构体MSG来传递给应用程序,参看MSDN

      

    MSG结构定义如下:

    typedef struct tagMSG {      

          HWND   hwnd;     

          UINT   message;                   //在windows中采用宏定义,以wm_开始(windows message)

          WPARAM wParam;              //附加参数,如键盘上字符的ASSCI码

          LPARAM lParam;     //附加参数

          DWORD  time;                 //消息发生的时间

          POINT  pt;      //消息发生时,光标所在位置

    } MSG;


    关于句柄:句柄(HANDLE),资源的标识。

      

      操作系统要管理和操作这些资源,都是通过句柄来找到对应的资源。按资源的类型,又可将句柄细分成图标句柄(HICON),光标句柄(HCURSOR),窗口句柄(HWND),应用程序实例句柄(HINSTANCE)、HDC:device context 的句柄等等各种类型的句柄。操作系统给每一个窗口指定的一个唯一的标识号即窗口句柄。
    从变量的类型区分变量的用途

    int x,y;

    x=30; 

    y=30; 

    //xy既可以用来表示坐标点,也可以用来表示宽度和高度,还可以用来表示身高和体重。

    typedef int WIDTH

    typedef int HEIGHT

    WIDTH x;

    HEIGHT y;

    //好处:我们从变量的类型上就可以知道xy是用来表示宽度和高度。

    WinMain函数Windows程序的入口函数,由系统调用
    int WINAPI WinMain(

    HINSTANCE hInstance,      // handle to current instance当前实例句柄

    HINSTANCE hPrevInstance,  // handle to previous instance先前的相同的实例句柄

    LPSTR lpCmdLine,          // command line命令行参数

    int nCmdShow              // show state最大化、最小化或者隐藏显示

    );


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

    1、设计一个窗口类;WNDCLASS

    2、注册窗口类;

    ATOM RegisterClass(
      __in  CONST WNDCLASS *lpWndClass
    );

    3、创建窗口; 

    HWND CreateWindow(
      __in  LPCTSTR lpClassName,       //窗口类名
      __in  LPCTSTR lpWindowName,  //窗口名
      __in  DWORD dwStyle,     //窗口类型,可最小化、最大化等,是一种特征位
      __in  int x,          //窗口显示的位置
      __in  int y,
      __in  int nWidth,       //窗口宽度
      __in  int nHeight,
      __in  HWND hWndParent,  
      __in  HMENU hMenu,
      __in  HINSTANCE hInstance,  //应用程序句柄
      __in  LPVOID lpParam     //WM_CREATE消息的附加参数
    );

    BOOL DestroyWindow(    //会发送WM_DESTROY消息 __in HWND hWnd );
    void PostQuitMessage(    //退出应用程序 __in int nExitCode      //WM_QUIT消息的附加信息 );

    4、显示及更新窗口。

    BOOL ShowWindow(
      __in  HWND hWnd,             //显示的窗口句柄
      __in  int nCmdShow    //显示窗口的状态(最大化、最小化SW_)
    );

    BOOL UpdateWindow( __in HWND hWnd );

    设计窗口类

      typedef struct _WNDCLASS {

      UINT      style;                     //窗口类型
      WNDPROC  lpfnWndProc;           //过程函数(回调函数)
      int       cbClsExtra;                 //窗口类附加内存,一般为0
      int       cbWndExtra;    //窗口附加内存,一般0
      HANDLE   hInstance;    //所属应用程序实例
         HICON     hIcon;     //LoadIcon(NULL,类型),NULL表示使用windows自带的图标;类型为icon的id
         HCURSOR   hCursor;
               HBRUSH    hbrBackground;
          LPCTSTR   lpszMenuName;
         LPCTSTR   lpszClassName;     //窗口类名,注册和创建窗口时使用
      } WNDCLASS;

    窗口类的类型
      在我们的程序中经常要用到一类变量,这个变量里的每一位(bit)都对应某一种特性。当该变量的某位为1时,表示有该位对应的那种特性,当该位为0时,即没有该位所对应的特性。当变量中的某几位同时为1时,就表示同时具有几种特性的组合。一个变量中的哪一位代表哪种意义,不容易记忆,所以我们经常根据特征的英文拼写的大写去定义一些宏,该宏所对应的数值中仅有与该特征相对应的那一bit)为1,其余的bit都为0。我们使用goto definition就能发现CS_VREDRAW=0x0001CS_HREDRAW=0x0002CS_DBLCLKS =0x0008CS_NOCLOSE=0x0200。他们的共同点就是只有一位为1,其余位都为0。如果我们希望某一变量的数值既有CS_VREDRAW特性,又有CS_HREDRAW特性,我们只需使用二进制OR|)操作符将他们进行或运算相组合,如style=CS_VREDRAW | CS_HREDRAW | CS_NOCLOSE。如果我们希望在某一变量原有的几个特征上去掉其中一个特征,用取反(~)之后再进行与(&)运算,就能够实现,如在刚才的style的基础上去掉CS_NOCLOSE特征,可以用style & ~CS_NOCLOSE实现。
    窗口过程函数
      第二个成员变量lpfnWndProc指定了这一类型窗口的过程函数,也称回调函数。回调函数的原理是这样的,当应用程序收到给某一窗口的消息时(还记得前面讲过的消息通常与窗口相关的吗?),就应该调用某一函数来处理这条消息。这一调用过程不用应用程序自己来实施,而由操作系统来完成,但是,回调函数本身的代码必须由应用程序自己完成。对于一条消息,操作系统到底调用应用程序中的哪个函数(回调函数)来处理呢?操作系统调用的就是接受消息的窗口所属的类型中的lpfnWndProc成员指定的函数。每一种不同类型的窗口都有自己专用的回调函数,该函数就是通过lpfnWndProc成员指定的
      举例:汽车厂家生产汽车好比应用程序创建窗口,用户使用汽车好比操作系统管理窗口,某种汽车在销售前就指定好了修理站(类似回调函数),当用户的汽车出现故障后(类似窗口收到消息),汽车用户(类似操作系统)自己直接找到修理站去修理,不用厂家(类似应用程序)亲自将车送到修理站去修理,但修理站还得由厂家事先建造好。
    消息处理函数
      获取消息:获得WM_QUIT消息时返回为0
    BOOL GetMessage(
      __out  LPMSG lpMsg,     //要被填充的消息结构体
      __in   HWND hWnd,       //获取哪一个窗口的消息,NULL表示获取应用程序所拥有的消息队列中的所有消息
      __in   UINT wMsgFilterMin,  //消息的最小值如WM_KEYFIRST,以一条消息
      __in   UINT wMsgFilterMax
    );
      转化消息:例如将WM_KEYDOWN和WM_KEYUP消息转换为WM_CHAR消息 
    BOOL TranslateMessage(
      __in  const MSG *lpMsg
    );
      分发消息:将消息发送给操作系统,再由系统发给窗口分发函数
    LRESULT DispatchMessage(
      __in  const MSG *lpmsg
    );

      消息盒子:弹出消息框,返回值为ID_?,如YES等
    int MessageBox(
      __in  HWND hWnd,    
      __in  LPCTSTR lpText,    //消息内容
      __in  LPCTSTR lpCaption,  //标题框
      __in  UINT uType      //消息框的类型如MB_OK
    );

    过程函数:  LRESULT为Long型
    LRESULT CALLBACK WindowProc(
      __in  HWND hwnd,   //窗口句柄
      __in  UINT uMsg,    //message id
      __in  WPARAM wParam,  
      __in  LPARAM lParam
    );
    LRESULT DefWindowProc( //默认的消息处理过程,必不可少,消息一旦产生之后,必须找到一个归宿 __in HWND hWnd, __in UINT Msg, __in WPARAM wParam, __in LPARAM lParam );
    绘制图形文字: 
      获得与释放窗口的DC(画布)
    HDC GetDC(
      __in  HWND hWnd
    );
    int ReleaseDC( __in HWND hWnd, __in HDC hDC );
      书写文字
    BOOL TextOut( __in HDC hdc,          __in int nXStart,      //文字的位置 __in int nYStart, __in LPCTSTR lpString,   //书写的字符串 __in int cbString      //字符串的长度 );  
      重新与结束绘制窗口:  只能用于响应WM_PAINT消息
    HDC BeginPaint(
      __in   HWND hwnd,
      __out  LPPAINTSTRUCT lpPaint
    );
    BOOL EndPaint( __in HWND hWnd, __in const PAINTSTRUCT *lpPaint );
    实例代码

      
     1 #include <windows.h>
    2 #include <stdio.h>
    3
    4 LRESULT CALLBACK WinSunProc(
    5 HWND hwnd, // handle to window
    6 UINT uMsg, // message identifier
    7 WPARAM wParam, // first message parameter
    8 LPARAM lParam // second message parameter
    9 );
    10
    11 int WINAPI WinMain(
    12 HINSTANCE hInstance, // handle to current instance
    13 HINSTANCE hPrevInstance, // handle to previous instance
    14 LPSTR lpCmdLine, // command line
    15 int nCmdShow // show state
    16 )
    17 {
    18 WNDCLASS wndcls;
    19 wndcls.cbClsExtra=0;
    20 wndcls.cbWndExtra=0;
    21 wndcls.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
    22 wndcls.hCursor=LoadCursor(NULL,IDC_CROSS);
    23 wndcls.hIcon=LoadIcon(NULL,IDI_ERROR);
    24 wndcls.hInstance=hInstance;
    25 wndcls.lpfnWndProc=WinSunProc;
    26 wndcls.lpszClassName="Weixin2003";
    27 wndcls.lpszMenuName=NULL;
    28 wndcls.style=CS_HREDRAW | CS_VREDRAW;
    29 RegisterClass(&wndcls);
    30
    31 HWND hwnd;
    32 hwnd=CreateWindow("Weixin2003","北京维新科学技术培训中心",WS_OVERLAPPEDWINDOW,
    33 0,0,600,400,NULL,NULL,hInstance,NULL);
    34
    35 ShowWindow(hwnd,SW_SHOWNORMAL);
    36 UpdateWindow(hwnd);
    37
    38 MSG msg;
    39 while(GetMessage(&msg,NULL,0,0))
    40 {
    41 TranslateMessage(&msg);
    42 DispatchMessage(&msg);
    43 }
    44 return 0;
    45 }
    46
    47 LRESULT CALLBACK WinSunProc(
    48 HWND hwnd, // handle to window
    49 UINT uMsg, // message identifier
    50 WPARAM wParam, // first message parameter
    51 LPARAM lParam // second message parameter
    52 )
    53 {
    54 switch(uMsg)
    55 {
    56 case WM_CHAR:
    57 char szChar[20];
    58 sprintf(szChar,"char is %d",wParam);
    59 MessageBox(hwnd,szChar,"weixin",0);
    60 break;
    61 case WM_LBUTTONDOWN:
    62 MessageBox(hwnd,"mouse clicked","weixin",0);
    63 HDC hdc;
    64 hdc=GetDC(hwnd);
    65 TextOut(hdc,0,50,"计算机编程语言培训",strlen("计算机编程语言培训"));
    66 ReleaseDC(hwnd,hdc);
    67 break;
    68 case WM_PAINT:
    69 HDC hDC;
    70 PAINTSTRUCT ps;
    71 hDC=BeginPaint(hwnd,&ps);
    72 TextOut(hDC,0,0,"维新培训",strlen("维新培训"));
    73 EndPaint(hwnd,&ps);
    74 break;
    75 case WM_CLOSE:
    76 if(IDYES==MessageBox(hwnd,"是否真的结束?","weixin",MB_YESNO))
    77 {
    78 DestroyWindow(hwnd);
    79 }
    80 break;
    81 case WM_DESTROY:
    82 PostQuitMessage(0);
    83 break;
    84 default:
    85 return
    DefWindowProc(hwnd,uMsg,wParam,lParam); //一定要加return呀,否则窗口不会显示
    86 }
    87 return 0;
    88 }
  • 相关阅读:
    ubuntu git配置文件,配置颜色,同义词
    A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS
    Lucene索引文件解析之“域”
    模2运算的原理
    Ubuntu aptcache命令查找可用软件包
    crc循环校验原理和实现
    python操作MySQL数据库
    异常:MessageBox.Show 容量超出了最大容量。参数名: capacity
    JavaScript中的 Dictionary 的用法
    IIS设置允许下载.h264文件解决方法
  • 原文地址:https://www.cnblogs.com/forlina/p/2116986.html
Copyright © 2020-2023  润新知