Visual C++
การจัดการกับ message

Message คืออะไร?
ดังที่ได้ กล่าวไปแล้ว ในบทก่อน ๆ ว่า วินโดวส์ จะติดต่อกับ โปรแกรม ที่เราเขียนขึ้น โดยการส่งข่าวสาร (message) ให้ ดังนั้น การจัดการกับ message นี้ จึงถือว่า เป็นหัวใจสำคัญ ของการเขียนโปรแกรม ในระบบวินโดวส์ เลยทีเดียว
message ในระบบวินโดวส์ มีจำนวนมากมาย ซึ่งจะใช้ค่าจำนวนเต็ม (interger value) 1 ค่า มาใช้แทน message หนึ่ง ๆ ตัวอย่างของ message ทั่ว ๆ ไป ที่พบได้บ่อย ก็เช่น
บางครั้ง message ที่วินโดวส์ ส่งมาให้กับโปรแกรมของเรา อาจจะมีค่า ข้อมูลอื่น ๆ เพิ่มเติมมาด้วย เช่น message WM_CHAR ที่จะถูกส่งมา เมื่อมีการกดแป้นพิมพ์ (keyboard) ก็จะมีข้อมูล เพิ่มเติม เป็น ค่าของคีย์ ที่ถูกกด ถูกส่งออกมาด้วย หรือ message WM_LBUTTONDOWN ที่จะถูกส่งมา เมื่อมีการ กดปุ่มซ้ายของเมาส์ลง ก็จะมีค่าตำแหน่ง โคออดิเนต ของเมาส์พอยน์เตอร์ ขณะนั้นถูกส่ง เพิ่มเติมกลับมาด้วย
แต่เนื่องจาก message ที่วินโดวส์ ส่งมาให้กับ โปรแกรมของเรานั้น มีจำนวนมากมายมหาศาล เรา ไม่จำเป็นต้อง ตอบสนองกับทุก message ที่ส่งมาก็ได้ เราจะเลือกตอบสนอง ต่อ message ที่เรา สนใจเท่านั้น ส่วน message ที่เราไม่ตอบสนองนั้น MFC จะเป็นตัวจัดการให้เอง

การจัดการกับ Message ในแบบของ MFC
การจัดการกับ messageในโปรแกรมของคุณ จะต้องมีขั้นตอน ดังต่อไปนี้
  1. ประกาศ message map ลงบนคลาสที่สืบทอดมาจากคลาสของหน้าต่างวินโดวส์ของเรา
  2. มีการเพิ่มบรรทัด ของ มาโครที่สัมพันธ์กับ message ที่เราสนใจ ลงไปใน message map
  3. มีการประกาศ ฟังก์ชั่นที่จะดำเนินการกับ message นั้น ๆ ไว้ใน class ของหน้าต่างโปรแกรม ของเรา
  4. มีการ เขียนรายละเอียด ของฟังก์ชั่น ที่จะดำเนินการ กับ message นั้นลงไปใน โปรแกรมของเรา

1. การประกาศ message map
จะทำได้โดยการพิมพ์บรรทัด DECLARE_MESSAGE_MAP( ) ลงไปในการประกาศคลาสที่สืบทอดมาจากคลาสหน้าต่าง ของวินโดวส์ของเรา

เช่น

/* Class    ของหน้าต่างโปรแกรมของเรา ที่สืบทอดมาจากคลาส CFrameWnd */    
class CMainWin : public CFrameWnd 
{
 public: 
 CMainWin( ); 
 DECLARE_MESSAGE_MAP(    ) /* ประกาศ message map ตรงท้ายสุดของการประกาศคลาส */    
 }; 

2. การเพิ่มมาโครที่สัมพันธ์กับ message ลงไปใน message map
การที่โปรแกรมของเราจะตอบสนองกับ message ที่วินโดวส์ ส่งมาให้ได้นั้น จะต้องมีการเพิ่มมาโครที่สัมพันธ์กับ message ที่เราต้องการลงไปใน message map ของโปรแกรมที่เราเขียนขึ้นนั้นเสียก่อน โดยมาโครของ message ในแบบของ MFC นั้น จะมีชื่อเดียวกันกับ message มาตรฐานของวินโดวส์ (standard Windows message) แต่จะขึ้นต้นด้วยคำว่า ON_ และปิดท้ายด้วยเครื่องหมายวงเล็บ() เช่น มาโครของ message WM_LBUTTONUP ก็คือ ON_WM_LBUTTONUP( ) , มาโครของ WM_PAINT ก็คือ ON_WM_PAINT( ) แต่มีข้อยกเว้นอยู่อย่างหนึ่ง คือ มาโครของ WM_COMMAND จะเป็น ON_COMMAND( )
การเพิ่มมาโครเข้าไปใน message map จะพิมพ์แทรกลงไประหว่างคำสั่ง BEGIN_MESSAGE_MAP และ END_MESSAGE_MAP ยกตัวอย่างเช่น ถ้าในโปรแกรม ของคุณ ต้องการจัดการกับ message WM_CHAR จะต้องพิมพ์บรรทัดต่อไปนี้ลงไปใน message map

BEGIN_MESSAGE_MAP(CMainWin,CFrameWnd)
  ON_WM_CHAR( )
END_MESSAGE_MAP( )

เนื่องจากในโปรแกรม ที่เราเชียนขึ้น สามารถม ีหน้าต่างวินโดวส์ ได้หลายหน้าต่าง และแต่ละหน้าต่างก็ สามารถมี message map ของตนเอง ทำให้เราสามารถ กำหนดได้ว่า message map ใดเป็นของวินโดวส์ใดได้ โดยในคำสั่ง BEGIN_MESSAGE_MAP จะมีพารามิเตอร์ 2 ตัวคือ Owner กับ Base ดังแสดงตัวอย่าง

 BEGIN_MESSAGE_MAP(Owner, Base)
   /* เว้นไว้เติมมาโครของ message ที่ต้องการจัดการ */
 END_MESSAGE_MAP( )

Owner ก็คือ ชื่อคลาสของ หน้าต่าง ที่จะจัดการกับ message นั้น
ส่วน
Base ก็คือชื่อคลาสแม่ (Base Class) ของคลาสที่จะจัดการกับ message นั้น


3. การประกาศฟังก์ชั่นที่เป็นตัวจัดการกับ message นั้น ๆ ลงในการประกาศคลาส ของหน้าต่างวินโดวส์ของเรา
ฟังก์ชั่นที่เป็นตัวจัดการกับ message เราเรียกอีกอย่างว่า message handler จะมีชื่อเดียวกับชื่อของ message ของเรา แต่จะขึ้นต้นด้วย คำว่า On แทน เช่น ตัวจัดการกับ message WM_CHAR ก็คือฟังก์ชั่นที่มีชื่อว่า OnChar( ) นั่นเอง หรือตัวจัดการกับ message WM_LBUTTONDOWN ก็คือฟังก์ชั่น OnLButtonDown() นั่นเอง ดังนั้น จากการประกาศคลาสในข้อที่ 1 ก็จะต้องมีการประกาศ ฟังก์ชั่น ที่เป็นตัว จัดการกับ message นั้นลงไปด้วย ดังนี้

/* Class    ของหน้าต่างโปรแกรมของเรา ที่สืบทอดมาจากคลาส CFrameWnd */
class    CMainWin : public CFrameWnd 
{
  public: CMainWin( );  
 afx_msg void OnChar(UINT ch, UINT count, UINT flags ); /* ประกาศฟังก์ชั่นที่เป็นตัวจัดการกับ message WM_CHAR */
 DECLARE_MESSAGE_MAP(    ) /* ประกาศ message map ตรงท้ายสุดของการประกาศคลาส */     
};

จะสังเกตว่าฟังก์ชั่น OnChar( ) จะมีคำว่า afx_msg ขึ้นต้นด้วย เพราะฟังก์ชั่นที่เป็นตัวจัดการ message ในแบบ MFC นี้จะต้อง ใช้คำว่า afx_msg เป็นตัวกำหนด ชนิดของฟังก์ชั่นเสมอ

4. เขียนรายละเอียดของฟังก์ชั่นที่เป็นตัวจัดการกับ message นั้น ๆ ลงในโปรแกรมของเรา
เช่นในการจัดการกับ message WM_CHAR โดยทุกครั้งเมื่อมีการกดปุ่มบนคีย์บอร์ด ในขณะที่หน้าต่างวินโดวส์ของโปรแกรมเรา active อยู่ จะมีตัวอักษรที่เรากด ไปปรากฎบน พื้นที่ทำงาน (client area) ของหน้าต่างวินโดวส์เรา ตรงตำแหน่งมุมบนซ้ายมือสุด เราจะเขียน รายละเอียด ของฟังก์ชั่นตัวจัดการ
message WM_CHAR ดังต่อไปนี้

char str[80]; /* กำหนดตัวแปร เป็นตัวเก็บ string ที่จะนำไปแสดง */
/* รายละเอียดของฟังก์ชั่นที่ใช้จัดการกับ message WM_CHAR */
afx_msg void CMainWin::OnChar(UINT ch, UINT count, UINT flags)
{
 CClientDC dc(this);
 dc.TextOut(1, 1,"    ", 3);    /* ลบตัวอักษรก่อนหน้านี้ */
 wsprintf(str, "%c", ch);      /* แปลงตัวอักษรที่ได้รับ ไปเป็น string */
 dc.TextOut(1, 1, str, strlen(str)); /* ส่ง string ที่ได้ออกวินโดวส์ */
}

คราวนี้ก็ลองมาดูตัวโปรแกรมทั้งหมดกัน อย่าลืม สร้างโปรเจคท์ใหม่ แล้วใช้คำสั่ง Project->setting จะได้ไดอะล็อกบ็อกซ์ขึ้นมา ในช่อง Microsoft Foundation Class ให้เลือกเป็น Use MFC in static library หรือ Use MFC in share dll ก็ได้ แล้วคลิ๊ก OK ก็จะออกมาที่หน้าจอทำงาน ตามปกติ

จากนั้นให้เลือกเมนู File -> New จะปรากฎ ไดอะลอกบอกซ์ใ้หเลือกชนิด ของไฟล์ใหม่ที่ต้องการสร้าง ให้เลือกเป็น C/C++ header file เพื่อจะสร้าง header file ขึ้นมา ตั้งชื่อ header file เป็น message.h แล้วคลิ๊ก OK จากนั้นก็เขียนโปรแกรมดังต่อไปนี้ลงใน หน้าต่างทำงาน

/* ไฟล์ ชื่อ message.h */
/* ประกาศคลาสของหน้าต่างวินโดวส์ของเรา ที่สืบทอดมาจากคลาส CFrameWnd */
class CMainWin : public CFrameWnd
{
 public:
 CMainWin( );
 afx_msg void OnChar(UINT ch, UINT count, UINT flags);
 DECLARE_MESSAGE_MAP( )
};
/* ประกาศคลาสของโปรแกรมเรา ที่สืบทอดมาจากคลาส CWinApp */
class CApp : public CWinApp
{
 BOOL InitInstance( );
};

save ไฟล์นี้ จากนั้นให้เลือกเมนู File -> New จะได้ ไดอะลอกบอกซ์ให้เลือกชนิดของไฟล์ ที่ต้องการสร้าง ตอนนี้ให้เลือกเป็น C/C++ Source File เพื่อเป็นการสร้าง source file ของ โปรแกรมของเรา ตั้งชื่อไฟล์เป็น message.cpp แล้วคลิ๊ก OK แล้วเขียนโปรแกรมต่อไปนี้ ลงในหน้าต่างทำงาน

/* ไฟล์ชื่อ message.cpp */
#include <afxwin.h> /* ต้อง include ไฟล์นี้ทุกครั้งที่เขียนแบบ MFC */
#include <string.h> /* include function ที่จัดการกับ string เข้ามา */
#include "message.h"/* include ไฟล์ที่ประกาศคลาสของเราเข้ามา */
char str[80];  /* กำหนดตัวแปร str เป็นอาเรย์ ของตัวแปรชนิด char จำนวน 80 ตัว */
/* ประกาศเป็นตัวแปรชนิด global เพื่อให้เรียกใช้ได้จากทุกแห่งในโปรแกรม */
CMainWin::CMainWin( )			/* function constructor ของคลาสหน้าต่างวินโดวส์เรา */
{
 Create(NULL, "Test WM_CHAR message map");
}
/* รายละเอียดของฟังก์ชั่น InitInstance */
BOOL CApp::InitInstance( )
{
 m_pMainWnd = new CMainWin;
 m_pMainWnd -> ShowWindow(m_nCmdShow);
 m_pMainWnd -> UpdateWindow( );
 return TRUE;
}

/* ส่วนของ message map ของหน้าต่างวินโดวส์ CMainWin ที่สืบทอดมาจากคลาส CFrameWnd*/
BEGIN_MESSAGE_MAP(CMainWin, CFrameWnd)
 ON_WM_CHAR( )
END_MESSAGE_MAP( )

/* รายละเอียดของฟังก์ชั่นที่ใช้จัดการ message WM_CHAR */
afx_msg void CMainWin::OnChar(UINT ch, UINT count, UINT flags)
{
 CClientDC dc(this);  /* สร้างออปเจคส์ของคลาส CClientDC ซึ่งจะรับค่า Device context ของ */
                                  /* client area ของหน้าต่างวินโดวส์นี้มาเก็บไว้ */
 dc.TextOut(1, 1, "   ", 3); /* เขียนข่องว่าง (space) ลงบน client area ตำแหน่งบนซ้ายสุด */
 wsprintf(str, "%c", ch); /* แปลงตัวอักขระ ch ไปเก็บไว้ที่ตัวแปรอาเรย์ str */
dc.TextOut(1, 1, str, strlen(str)); /* เขียนตัวอักขระลงบน client area ตำแหน่งบนซ้ายสุด */
}
CApp myapp; /* สร้างออปเจคท์ของคลาสโปรแกรมของเรา */

จากนั้น save ไฟล์ แล้วทำการ build โปรแกรมตามที่ได้กล่าวไว้แล้วในบทก่อน แล้วทำการ execute โปรแกรม ก็จะได้หน้าต่างโปรแกรมขึ้นมา ลองกดปุ่มตัว อักษรใด ๆ ก็ได้บนคีย์บอร์ด จะได้ตัวอักษรปรากฎบนมุมบนซ้ายของ พื้นที่ทำงาน (Client area) ของวินโดวส์ ดังแสดงในรูป

ตอนนี้โปรแกรม ของเรา ก็ดูจะทำงานได้ เหมือนที่ต้องการ แต่จริง ๆ แล้ว ยังมีข้อบกพร่อง อย่างหนึ่ง อยู่ในโปรแกรมนี้ เพื่อน ๆ พอจะทราบไม๊คะ? ถ้ายังไม่ทราบ ก็ลอง minimize หน้าต่าง ของโปรแกรม ที่เราเขียนขึ้น ลงไปก่อนนะคะ แล้วลอง restore หน้าต่างวินโดวส์ ของเราให้กลับขึ้นมา แสดงบนจอภาพใหม่ จะพบว่า ตัวอักษรที่อยู่ ด้านบนซ้าย ของพื้นที่ทำงาน บนวินโดวส์ หายไป ที่เป็นเช่นนี้ เพราะว่า วินโดวส์จะไม่มีการ เก็บข้อมูลที่แสดง บนพื้นที่ทำงานไว้ เมื่อถูก minimize หรือเมื่อถูกหน้าต่าง ของวินโดวส์อื่น ๆ ทับ จึงเป็นหน้าที่ ของผู้เขียนโปรแกรม ที่จะต้องเขียน code ตรงส่วนนี้เอง ซึ่งเราจะกล่าวถึงต่อไป ในบทที่ 6 นะคะ

แล้วพบกันไหม่ คราวหน้าค่ะ หนูแจ๋ว

Hosted by www.Geocities.ws

1