บทที่ 6: การรับข้อมูลจากคีย์บอร์ด

ข้อมูลจาก Iczelion

บทนี้เราจะมาศึกษากันถึงการรับข้อมูลจากคีย์บอร์ด

หลักการ

ในระบบปฏิบัติการวินโดวส์สามารถจะรันโปรแกรมหลายๆ โปรแกรมได้พร้อม ๆ กัน แต่ว่าเครื่องคอมพิวเตอร์ของเรานั้นจะมีคีย์บอร์ดแค่อันเดียว ดังนั้นระบบจะเป็นตัวจัดการส่งข้อมูลที่เราพิมพ์ให้กับวินโดว์ที่กำลังได้รับโฟกัสอยู่ ซึ่งก็จะมีเพียงวินโดว์เดียวที่จะได้รับข้อมูลนี้ในขณะนั้น

จริงๆ แล้วข้อมูลของคีย์บอร์ดแบ่งออกได้เป็น 2 ประเภทหลักๆ ขึ้นอยู่กับว่าคุณจะมองคีย์บอร์ดอย่างไร ถ้าคุณมองคีย์บอร์ดเป็นที่รวบรวมของคีย์ต่างๆ เมื่อคุณกดคีย์บอร์ด วินโดวส์จะส่งเมสเสจ WM_KEYDOWN ให้กับวินโดว์ที่กำลังรับโฟกัสอยู่เพื่อบอกว่ามีการกดคีย์บอร์ด เมื่อคุณปล่อยนิ้วจากคีย์บอร์ด วินโดวส์ก็จะส่งเมสเสจ WM_KEYUP ให้กับวินโดว์นั้น แต่ถ้าคุณมองคีย์บอร์ดเป็นอุปกรณ์สำหรับป้อนตัวอักษร เมื่อคุณกดคีย์บอร์ดวินโดวส์จะส่งเมสเสจ WM_CHAR ไปให้กับวินโดว์ที่กำลังรับโฟกัสอยู่เพื่อบอกว่าผู้ใช้ได้ส่งตัวอักษรไปให้ แต่จริงๆ แล้ววินโดวส์ส่งเมสเสจ WM_KEYDOWN และ WM_KEYUP ไปให้ จากนั้นเมสเสจทั้งสองนี้ก็จะถูกแปลงเป็นเมสเสจWM_CHAR โดยการเรียกใช้ฟังก์ชั่น TranslateMessage โพรซิเยอร์ของวินโดว์จะเป็นตัวตัดสินใจว่าจะใช้งานเมสเสจทั้งสามนี้หรือเลือกเอาเฉพาะเมสเสจที่มันต้องการ แต่ส่วนใหญ่แล้วคุณไม่ต้องส่นใจเมสเสจ WM_KEYDOWN และ WM_KEYUP ก็ได้ เพราะว่าฟังก์ชั่น TranslateMessage ในเมสเสจลูปจะแปลงเมสเสจทั้งสองนี้ให้เป็นเมสเสจ WM_CHAR อยู่แล้ว ดังนั้นบทนี้จึงเน้นที่ WM_CHAR

ตัวอย่างโปรแกรม

.386 .model flat,stdcall option casemap:none WinMain proto :DWORD,:DWORD,:DWORD,:DWORD include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc include \masm32\include\gdi32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\gdi32.lib .data ClassName db "SimpleWinClass",0 AppName db "Our First Window",0 char WPARAM 20h ; ตัวอักษรที่วินโดว์รับจากคีย์บอร์ด .data? hInstance HINSTANCE ? CommandLine LPSTR ? .code start: invoke GetModuleHandle, NULL mov hInstance,eax invoke GetCommandLine mov CommandLine,eax invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT invoke ExitProcess,eax WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD LOCAL wc:WNDCLASSEX LOCAL msg:MSG LOCAL hwnd:HWND mov wc.cbSize,SIZEOF WNDCLASSEX mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc, OFFSET WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,NULL push hInst pop wc.hInstance mov wc.hbrBackground,COLOR_WINDOW+1 mov wc.lpszMenuName,NULL mov wc.lpszClassName,OFFSET ClassName invoke LoadIcon,NULL,IDI_APPLICATION mov wc.hIcon,eax mov wc.hIconSm,eax invoke LoadCursor,NULL,IDC_ARROW mov wc.hCursor,eax invoke RegisterClassEx, addr wc invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\ WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\ CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\ hInst,NULL mov hwnd,eax invoke ShowWindow, hwnd,SW_SHOWNORMAL invoke UpdateWindow, hwnd .WHILE TRUE invoke GetMessage, ADDR msg,NULL,0,0 .BREAK .IF (!eax) invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg .ENDW mov eax,msg.wParam ret WinMain endp WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM LOCAL hdc:HDC LOCAL ps:PAINTSTRUCT .IF uMsg==WM_DESTROY invoke PostQuitMessage,NULL .ELSEIF uMsg==WM_CHAR push wParam pop char invoke InvalidateRect, hWnd,NULL,TRUE .ELSEIF uMsg==WM_PAINT invoke BeginPaint,hWnd, ADDR ps mov hdc,eax invoke TextOut,hdc,0,0,ADDR char,1 invoke EndPaint,hWnd, ADDR ps .ELSE invoke DefWindowProc,hWnd,uMsg,wParam,lParam ret .ENDIF xor eax,eax ret WndProc endp end start

วิเคราะห์โปรแกรม

char WPARAM 20h ; ตัวอักษรที่วินโดว์รับจากคีย์บอร์ด

char เป็นตัวแปรสำหรับเก็บตัวอักษรที่รับมาจากคีย์บอร์ด เพราะว่าตัวอักษรจะถูกส่งมาในรูปแบบ WPARAM ในโพรซิเยอร์ WndProc ของวินโดว์ ดังนั้นเพื่อความสะดวกจึงสร้างให้มีรูปแบบเป็น WPARAM ส่วนค่าเริ่มต้น 20h นั้น เป็นค่าของรหัสช่องว่าง สำหรับให้พื้นที่แสดงผลแสดงช่องว่างในตอนที่วินโดว์เริ่มรันครั้งแรก


.ELSEIF uMsg==WM_CHAR 
	push wParam 
	pop  char 
	invoke InvalidateRect, hWnd,NULL,TRUE 
		

มีฟังก์ชั่นเพิ่มเติมที่วินโดว์ใช้จัดการกับเมสเสจ WM_CHAR มันจะเก็บตัวอักษรในตัวแปร char แล้วจึงเรียกฟังก์ชั่น InvalidateRect ฟังก์ชั่นนี้จะทำให้พื้นที่ทำงานที่กำหนดมีสถานะใช้การไม่ได้ซึ่งจะบังคับให้วินโดวส์ส่งเมสเสจ WM_PAINT ไปยังโพรซิเยอร์ของวินโดว์ รูปแบบของฟังก์ชั่นนี้คือ


InvalidateRect proto hWnd:HWND,\ 
	lpRect:DWORD,\ 
	bErase:DWORD 
		

lpRect เป็นพอยเตอร์ที่ชี้ยังยังพื้นที่สี่เหลี่ยนในพื้นที่ทำงานที่เราต้องการกำหนดให้มีสถานะใช้งานไม่ได้ ถ้ามีค่าเป็น null พื้นที่นั้นจะหมายถึงพื้นที่ทำงานทั้งหมด

bErase เป็นแฟล็กที่บอกวินโดวส์ว่าจะลบพื้นที่ด้านหลังด้วยหรือไม่ ถ้ามีค่าเป็น TRUE วินโดวส์จะลบพื้นที่ด้านหลังของพื้นที่ที่ถูกกำหนดเมื่อฟังก์ชั่น BeginPaint ถูกเรียก

ดังนั้นเราจึงวางแผนโดยการเก็บข้อมูลที่จำเป็นในการวาดลงในพื้นที่ทำงานแล้วจึงส้รางเมสเสจ WM_PAINT เพื่อให้ทำการวาดพื้นที่ทำงานใหม่ แน่นอนว่าเราต้องทราบโค้ดในส่วนของ WM_PAINT ก่อนว่าจะทำอะไร

จริง ๆ แล้วเราสามารถวาดลงในพื้นที่ทำงานระหว่างที่จัดการเมสเสจ WM_CHAR โดยการใช้คู่ของฟังก์ชั่น GetDC และ ReleaseDC ได้ แต่จะไม่สามารถวาดตัวอักษรของเราในพื้นที่ทำงานใหม่ได้ ที่ให้ทำงานในส่วนของ WM_PAINT เพราะว่าเราสามารถสั่งให้วินโดวส์ทำการวาดพื้นที่ทำงานใหม่โดยการส่งเมสเสจ WM_PAINT ณ สถานที่และเวลาใดก็ได้ที่เราต้องการ


invoke TextOut,hdc,0,0,ADDR char,1 
		

เมื่อฟังก์ชั่น InvalidateRect ถูกเรียก มันจะส่งเมสเสจ WM_PAINT กลับไปยังโพรซิเยอร์ของวินโดว์ ดังนั้นโค้ดในส่วนของ WM_PAINT จึงถูกเรียกให้ทำงาน ฟังก์ชั่น BeginPaint จึงสร้างแฮนเดิ้ลของดีไวซ์คอนเท็กซ์แล้วจึงเรียกฟังก์ชั่น TextOut เพื่อวาดตัวอักษรของเราในพื้นที่ทำงานที่ x=0และ y=0 เมื่อคุณรันโปรแกรมและกดคีย์ใดๆ จะเห็นตัวอักษรปรากฏขึ้นที่มุมบนซ้ายของพื้นที่ทำงาน เมื่อวินโดว์ถูกย่อขนาดแล้วขยายขึ้นใหม่ ตัวอักษรก็จะยังคงอยู่ที่เดิมเพราะว่าโค้ดและข้อมูลที่จำเป็นทั้งหมดในการวาดใหม่รวมอยู่ในส่วนของ WM_PAINT


กลับหน้าแรก