No MFC 编程06 - 检测来自系统的消息

类别:VC语言 点击:0 评论:0 推荐:

 

     ( 之前的一篇教程如果有什么不明白,不要紧,请继续往下走,以后回过头看就自然不会再迷糊! )

     Windows 用的是基于事件驱动的编程方法,所以检测并处理消息是很重要的。现在让我们继续研究一下消息队列 (Queue) :

     一个更加生动的例子!  系统 和 我 之间隔着一条河,系统在上游,我则处在下游并设置了一个水闸。  系统有什么要通知我就写到瓶子里,扔进河中。  最后这些瓶子在我设的水闸前排起队来了。  (这个例子不错嘛)    我有空的时候就捞起一个瓶子,读出里面的指示,接着就知道下一步要做些什么了。 

     如果我没有空怎么办?  那么你所处理的消息有可能已经错过了最佳时机。    有一个办法可以让你的消息插队,那就是用 PostMessage() 。


     好了,先来动动手,模仿 系统 给我的程序的 消息队列 发送消息。

     要用到的函数:   发送消息到队列 - PostThreadMessage() ,   检索队列中消息 - PeekMessage() ,还有一个装载消息和相关数据的结构 MSG (也就是装指令的瓶子),具体如下:

typedef struct tagMSG {
    HWND        hwnd;      // 与该条消息相关的窗口句柄,为 0 则没有相关的窗口
    UINT        message;   // 消息的具体数值,相当于没符号的 int (16位),范围是 0 — 65535
    WPARAM      wParam;    // 第一个相关参数,类型同上。
    LPARAM      lParam;    // 第二个相关参数,相当于 long 定义,范围 (略)
    DWORD       time;      // 系统计时,相当于 unsigned long ,用 GetTickCount() 可以取得相同的值
    POINT       pt;        // 鼠标现时的坐标,相当于 ( LONG  x;  LONG  y; ) 坐标
} MSG;


     具体的源程序见下面:


 //  File Name:  WinMain.cpp


 #define WIN32_LEAN_AND_MEAN   // Say No to MFC !!

  #include <windows.h>


 char Temp[177] = "Hello world";  // 临时字符串变量

 char Result[512] = "";   //  用于保存、输出结果

 char Title[] = "Sample for Message details                   __CopyRight - `海风                `";

 

 


// Name: WinMain()  // 主程序入口
// ------ ---------- ----------- ---------
int WINAPI WinMain( HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow )
{

 

   MSG  msg;  // 定义了一个装载消息的结构

   DWORD CurThreadID = GetCurrentThreadId( );  // 取得当前线程的 ID (标志号)

    MessageBox( NULL, "        请按 确定 开始测试!", Title, MB_OK | MB_TOPMOST );


 BOOL  done = false;  // 用来保存 发送消息 是否成功


   // 发送消息需用线程的 ID 指示要送到哪个消息队列中去,ID 由 GetCurrentThreadId() 取得

   done = PostThreadMessage( CurThreadID, 123 ,   1, 22 ); 
      if (! done  )  MessageBox( NULL, "发送消息失败_1!", Title, MB_OK | MB_TOPMOST );

  
   done = PostThreadMessage( CurThreadID, 345 ,  2, 22 );
      if (! done  )  MessageBox( NULL, "发送消息失败_2!", Title, MB_OK | MB_TOPMOST );

   done = PostThreadMessage( CurThreadID, 4321 , 3, 22 );
      if (! done  )  MessageBox( NULL, "发送消息失败_3!", Title, MB_OK | MB_TOPMOST );

 


   //  以下这个发送消息的效果相当于 PostQuitMessage( 12 );
   done = PostThreadMessage( CurThreadID, WM_QUIT , 12, 0 );
      if (! done  )  MessageBox( NULL, "发送消息失败_4!", Title, MB_OK | MB_TOPMOST );


     PostQuitMessage( 12 );  // 发送退出消息

  //   相当于发送了 WM_QUIT 消息,但只有 GetMessage() 会检测它,PeekMessage() 则要外加判断来处理。
  //   WM_QUIT 的值等于 18 (即0x0012),点击鼠标右键看 "Go To Definition Of WM_QUIT"
    


   long i = 24, k = 0;  // i 为循环系数初始值, k 用来纪录接受了多少条消息

   while (i) // 循环系数 i 为 0 则退出
   {
      if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
     {

            wsprintf( Temp , "检测到 第 %ld 号 消息 \n       "
                             " 第一参数 wParam = %ld ;"
                             " 第二参数 lParam = %ld ;\n       "
                             " (系统时间)  msg.time = %ld ;    "
                             " (涉及的句柄) msg.hwnd = %ld \n\n",
                             msg.message, msg.wParam, msg.lParam, msg.time, msg.hwnd );
            lstrcat( Result, Temp );  k++;  // 追加到结果字符串后面
        
      }

      i--;  // 循环系数减少 1,为 0 则退出
   }


     wsprintf( Temp , "共收到 %ld 条消息\n", k );  // 整理报告数字
     lstrcat( Result, Temp );

 

   MessageBox( NULL, Result, Title, MB_OK | MB_TOPMOST );  // 显示检测到的消息详细情况


   ExitProcess(0);
   return NULL;
   }

 

    以上这个源程序要注意两个地方:

       其一是   PeekMessage() 的最后一个参数 PM_REMOVE ,如果换成 PM_NOREMOVE ,则虽然取得了消息,但仍保留一份在消息队列里面 (MSDN 原文:Messages are not removed from the queue after processing by PeekMessage. ),会有什么效果?你自己换一下参数试一下吧。

       其二是   如果使用  PostQuitMessage()  函数,其实际效果相当于用  PostThreadMessage()  向你的线程发送 WM_QUIT 消息,你可以在消息泵里判断 如果 ( msg.message == WM_QUIT ) 为真就退出 消息(处理)循环 (使用 break ) 。

    另外,在上面的例子中改用 PM_NOREMOVE 来测试,如果得不到预期的结果,我一点都不觉得奇怪,建议采用下面的方法来显示结果。


   while (true)   // 清除所有多余消息
  {  if(! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))  break;   }

    MessageBox( NULL, Result, Title, MB_OK | MB_TOPMOST );  // 显示检测到的消息详细情况

 


    下面我们将继续尝试检测真正来自系统给我们的消息!


    不过在此之前,我们需要先简单介绍一下句柄,因为一会儿要创建的新窗口就有一个。

    句柄是翻译过来的名词 (英文是 Handle ) ,可是它一点都不好帮助理解!

    我的一个朋友发表了他的看法 " 句柄就好像一个苹果的小柄,如果你要吃苹果... "  另一个朋友打断了他 "  什么嘛,应该是这样!你做了亏心事,让我拿住了把柄,嘿嘿嘿,我要你做什么你都要听我的... "

    对! 我看就是这么回事,比方说创建了一个窗口,  你要知道它的状态,大小,位置等,你要有句柄; 你要改变它的属性,改变它的式样,你也要有句柄才行; 就算你要毁了它,一样要有它的句柄才成事。  既然句柄那么重要,那么什么时候可以得到句柄呢? 

    一般来说,当你使用 CreateWindowEx() 函数创建一个新窗口的时候,函数会返回一个,记得要保存它!

    ( 一个提示: 句柄并不是只是窗口才有的,许多 windows 里面的对象都用句柄来访问, 比如你向系统申请内存, 就要有一个堆的句柄 (handle of heap) 等等,与句柄相关的内容以后还会提到。 )

    接着我们本篇的主题 - 检测来自系统的消息 。

    ( 以下例子使用 CreateWiondowEx() 函数创建一个新窗口,关于该函数具体用法以后会作详细说明 )

    在开始之前,我们要先大概有一个印象,系统会发什么事件给我们? 什么时候会发给我们?

    先回答后面一个问题,一般来说,当我们的程序获得输入焦点,也就是程序的窗口处于最上面,且标题变成蓝色底色,系统会把输入信息发给我们的程序来处理。

    会有些什么样的输入信息呢?大概是这些,比方说,我们按动了键盘,当我们按下键盘上一个键,系统会向 程序(当前输入焦点) 发送一个 WM_KEYDOWN 的信息,(WM_KEYDOWN == 256),而我们松开键的时候系统会发出 WM_KEYUP (WM_KEYUP == 257,通过查看16位的 msg.wParam 可以区分按下的是哪一个键 ),类似的还有 WM_MOUSEMOVE 和 WM_LBUTTONDOWN 等与鼠标输入有关的信息。


    在下面的例子中,在按下 确定 继续以后,你有三秒时间令系统向你的程序发送输入信息!

 

//  File Name:  WinMain.cpp


 #define WIN32_LEAN_AND_MEAN   // Say No to MFC !!

  #include <windows.h>


 char Temp[177] = "Hello world";

 char Result[1024] = "";   //  用于保存、输出结果

 char Title[] = "Sample for Message details                   __CopyRight - `海风  ...............";

 


// Name: WinMain() // 主程序入口
// ------ ---------- ----------- ---------
int WINAPI WinMain( HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow )
{

 

   MSG  msg;  // 定义了一个装载消息的结构

 

  //* 

   //  下面用 CreateWindowEx() 函数创建一个临时窗口 , 使用 hWnd 暂时保存句柄
   HWND hWnd=CreateWindowEx( WS_EX_TOPMOST | WS_EX_TOOLWINDOW ,
              "Edit","1231",
              WS_OVERLAPPEDWINDOW,
              120,120,
              280,180,
              NULL,NULL,hInstance,NULL);
  

    ShowWindow( hWnd, SW_SHOW );  // 让这个窗口可以被看见

    MessageBox( NULL, "创建了测试窗口,按 确定 继续测试!", Title, MB_OK | MB_TOPMOST );
  // */


    Sleep(3000);  // 利用这段时间在新窗口上触发一些如 鼠标移动,按下键钮 等事件

 

  //  先发送三条自定义消息

 BOOL  done = false;  // 用来保存 发送 是否成功

  // 当前线程的 ID 由 GetCurrentThreadId() 取得
   done = PostThreadMessage( GetCurrentThreadId(), 123 , 1, 22 );
      if (! done  )  MessageBox( NULL, "发送消息失败_1!", Title, MB_OK | MB_TOPMOST );


   // PostMessage() 发送的是与窗口相关的消息
   done = PostMessage( hWnd, WM_KEYDOWN , 55, 4321 );  // 所发送的是假的 WM_KEYDOWN 消息
      if (! done  )  MessageBox( NULL, "发送消息失败_2!", Title, MB_OK | MB_TOPMOST );

 

     PostQuitMessage( true );  // 发送退出消息
     // 只有 GetMessage() 可以检测它,PeekMessage() 要外加判断
    


  //  以下开始检测系统消息

   long i = 24, k = 0;  // i 为循环系数初始值, k 用来纪录接受了多少条消息

   while (i) // 循环系数 i 为 0 则退出
   {
      if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
     {

            wsprintf( Temp , "检测到 第 %ld 号 消息 \n        "
                             " 第一参数 wParam = %ld ;"
                             " 第二参数 lParam = %ld ;\n        "
                             " (系统时间)  msg.time = %ld ;"
                             " (涉及的句柄) msg.hwnd = %ld \n\n",
                             msg.message, msg.wParam, msg.lParam, msg.time, msg.hwnd );
            lstrcat( Result, Temp );  k++;  // 追加到结果字符串后面
        
      }

      i--;  // 循环系数减少 1,为 0 则退出
   }


     wsprintf( Temp , "共收到 %ld 条消息\n", k );  // 整理报告数字
     lstrcat( Result, Temp );


   DestroyWindow( hWnd );   // 退出程序前要销毁窗口

   MessageBox( NULL, Result, Title, MB_OK | MB_TOPMOST );  // 显示截获消息的详细内容
           // 请细心的读者比较一下消息的系统时间,是不是有插队的情况发生呢?

   ExitProcess(0);
   return NULL;
   }

 


     在以上实验中,如果曾按下键钮,应该会收到 WM_KEYDOWN (第 256 号消息),和 WM_KEYUP (第 257 号消息),它们会成对的出现。


     不过,出现了 第 280 号 消息( msg.message == 0x0118 ),我搞不懂那是什么消息来着。

 

      ……      `海风   2002年10月17日 am 2:01         

——————————————————————————

     学习的秘诀是要知道 Why ,而不仅仅是 How  ...

目前喜欢的歌:  動力火車 - 忠孝东路走九遍

 

本文地址:http://com.8s8s.com/it/it2243.htm