kowala's home

kowala's home
這裡是我的學習筆記,陸續增加中。
http://kowala21.blogspot.com

2012-07-27

wxDev C++ 使用 winFrame 視窗+測試鍵盤 wxKeyEvent 之2

轉載請註明出處,謝謝。

kowala's home
http://kowala21.blogspot.com

前一篇 wxDev C++ 使用 winFrame 視窗+測試鍵盤 wxKeyEvent 之1

我們必須先取出按鍵值,然後解析它。這裡的目標是要把全部的按鍵都抓到,先區分按鍵為可列印按鍵(printable character)或是擴展按鍵(special key),可以這樣來區分[1]。

int code=event.GetModifiers();//組合鍵狀態
int keyCode = event.GetKeyCode();//ascii 鍵值
if ( wuChar != WXK_NONE && code == 0){ //一般鍵
    wxLogMessage("printable key '%c'", event.GetUnicodeKey());
}else{//組合鍵
    wxLogMessage("special key '%c'", event.GetUnicodeKey());
}

在一般鍵裡頭,包含有命令鍵,如 Enter,TAB,BACK等等,它並不可視,所以要加一個 switch () 來把它們分離出來,我使用一個字串( TCHAR str[] )來幫忙顯示結果,在wxDev C++之中,似乎是使用 unicode,這意味著,使用中文會有亂碼問題[2]。

int code=event.GetModifiers();//組合鍵狀態
int keyCode = event.GetKeyCode();//ascii 鍵值
TCHAR str[]={0};
if ( wuChar != WXK_NONE && code == 0){ //一般鍵
        switch (keyCode){
            case WXK_BACK://8
                wsprintf(str,L"Key : %s",L"BACK");
                MessageBox(0,str,L"Normal Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_TAB://9
                wsprintf(str,L"Key : %s",L"TAB");
                MessageBox(0,str,L"Normal Key",MB_ICONINFORMATION|MB_OK);
                break;
            ......
            default:
                wsprintf(str,L"Key : %c",keyCode);
                MessageBox(0,str,L"Normal Key",MB_OK);
        }
}else{//組合鍵
    wxLogMessage("special key '%c'", event.GetUnicodeKey());
}

執行結果



到這裡,我們已經處理了一般按鍵,接著要來處理組合鍵及特別鍵,像是 Shift+key,Ctrl+Key,PageUP,F1,F2...
組合鍵說明(code 的組合)
Alt=1
Ctrl=2
Shift=4
所以
Ctrl+Alt = 2+1 = 3
Shift+Ctrl = 4+2 = 6
Shift+Ctrl+Alt = 4+2+1 = 7
...
組合鍵要判斷兩次,要判斷 code 的組合,再取 keyCode 鍵值。

wxDevC++已經定義了一些虛擬鍵值,請參考 Type KeyEvent [3] .
為了保持簡潔,把它獨立出來,另外寫個 chkExtKey() 如下。

void chkExtKey(int keyCode){//擴展鍵+組合鍵
    TCHAR str[100]={0};
    switch (keyCode){
            case 324://NumLock 0 = 324
                _stprintf(str,L"code = %d, key = %c",keyCode,'0');
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_NUMPAD1://NumLock 1 = 325
                _stprintf(str,L"code = %d, key = %c",keyCode,'1');
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            ..............
            default:               
                _stprintf(str,L"(key,code)=(%c,%d)",keyCode,keyCode);
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
    }
}

執行結果




使用方法

int code=event.GetModifiers();//組合鍵狀態
int keyCode = event.GetKeyCode();//ascii 鍵值
TCHAR str[]={0};
if ( wuChar != WXK_NONE && code == 0){ //一般鍵
        switch (keyCode){...}

}else{//組合鍵
    switch (code){ //code 的組合
            //(Shift,Ctrl,Alt)=(4,2,1)
            case 1:
                MessageBox(0,L"Alt",L"Func Key",MB_ICONINFORMATION|MB_OK);
                break;
            case 2:
                MessageBox(0,L"Ctrl",L"Func Key",MB_ICONINFORMATION|MB_OK);
                break;
            case 3:
                MessageBox(0,L"Ctrl+Alt",L"Func Key",MB_ICONINFORMATION|MB_OK);
                break;
            case 4:
                MessageBox(0,L"Shift",L"Func Key",MB_ICONINFORMATION|MB_OK);
                break;
            case 5:
                MessageBox(0,L"Shift+Alt",L"Func Key",MB_ICONINFORMATION|MB_OK);
                break;
            case 6:
                MessageBox(0,L"Shift+Ctrl",L"Func Key",MB_ICONINFORMATION|MB_OK);
                break;
            case 7:
                MessageBox(0,L"Shift+Ctrl+Alt",L"Func Key",MB_ICONINFORMATION|MB_OK);
                break;          
        }
    chkExtKey(keyCode);//擴展鍵
}

完整代碼

/*
 * kbTestFrmKeyDown
 */
void chkExtKey(int keyCode){//擴展鍵
    TCHAR str[100]={0};
    switch (keyCode){
            case 324://NumLock 0 = 324
                _stprintf(str,L"code = %d, key = %c",keyCode,'0');
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_NUMPAD1://NumLock 1 = 325
                _stprintf(str,L"code = %d, key = %c",keyCode,'1');
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_NUMPAD2://NumLock 2 = 326
                _stprintf(str,L"code = %d, key = %c",keyCode,'2');
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_NUMPAD3://NumLock 3 = 327
                _stprintf(str,L"code = %d, key = %c",keyCode,'3');
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_NUMPAD4://NumLock 4 = 328
                _stprintf(str,L"code = %d, key = %c",keyCode,'4');
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_NUMPAD5://NumLock 5 = 329
                _stprintf(str,L"code = %d, key = %c",keyCode,'5');
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_NUMPAD6://NumLock 6 = 330
                _stprintf(str,L"code = %d, key = %c",keyCode,'6');
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_NUMPAD7://NumLock 7 = 331
                _stprintf(str,L"code = %d, key = %c",keyCode,'7');
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_NUMPAD8://NumLock 8 = 332
                _stprintf(str,L"code = %d, key = %c",keyCode,'8');
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_NUMPAD9://NumLock 9 = 333
                _stprintf(str,L"code = %d, key = %c",keyCode,'9');
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_F1://F1
                _stprintf(str,L"code = %d, key = %s",keyCode,L"F1");
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_F2://F2
                _stprintf(str,L"code = %d, key = %s",keyCode,L"F2");
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_NUMLOCK://NUMLOCK
                _stprintf(str,L"code = %d, key = %s",keyCode,L"NUMLOCK");
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_PAGEUP://PAGEUP
                _stprintf(str,L"code = %d, key = %s",keyCode,L"PAGEUP");
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_SPECIAL1://SPECIAL1
                _stprintf(str,L"code = %d, key = %s",keyCode,L"SPECIAL1");
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
                break;
            default:               
                _stprintf(str,L"(key,code)=(%c,%d)",keyCode,keyCode);
                MessageBox(0,str,L"Extend Key",MB_ICONINFORMATION|MB_OK);
    }
}
void kbTestFrm::kbTestFrmKeyDown(wxKeyEvent& event){
    int code=event.GetModifiers();//組合鍵狀態,(Shift,Ctrl,Alt)=(4,2,1)
    int keyCode = event.GetKeyCode();//ascii 鍵值
    wxUint32 wuChar=event.GetUnicodeKey();//unicode鍵值
    TCHAR str[]={0};
    if ( wuChar != WXK_NONE && code == 0){//一般鍵
        switch (keyCode){
            case WXK_BACK://8
                wsprintf(str,L"Key : %s",L"BACK");
                MessageBox(0,str,L"Normal Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_TAB://9
                wsprintf(str,L"Key : %s",L"TAB");
                MessageBox(0,str,L"Normal Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_RETURN://13
                wsprintf(str,L"Key : %s",L"RETURN");
                MessageBox(0,str,L"Normal Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_ESCAPE://27
                wsprintf(str,L"Key : %s",L"ESCAPE");
                MessageBox(0,str,L"Normal Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_SPACE://32
                wsprintf(str,L"Key : %s",L"SPACE");
                MessageBox(0,str,L"Normal Key",MB_ICONINFORMATION|MB_OK);
                break;
            case WXK_DELETE://127
                wsprintf(str,L"Key : %s",L"DELETE");
                MessageBox(0,str,L"Normal Key",MB_ICONINFORMATION|MB_OK);
                break;
            default:
                wsprintf(str,L"Key : %c",keyCode);
                MessageBox(0,str,L"Normal Key",MB_OK);
        }
    }else{//組合鍵 (Shift,Ctrl,Alt)=(4,2,1)
        switch (code){//code 的組合            //(Shift,Ctrl,Alt)=(4,2,1)
            case 1:
                MessageBox(0,L"Alt",L"Func Key",MB_ICONINFORMATION|MB_OK);
                break;
            case 2:
                MessageBox(0,L"Ctrl",L"Func Key",MB_ICONINFORMATION|MB_OK);
                break;
            case 3:
                MessageBox(0,L"Ctrl+Alt",L"Func Key",MB_ICONINFORMATION|MB_OK);
                break;
            case 4:
                MessageBox(0,L"Shift",L"Func Key",MB_ICONINFORMATION|MB_OK);
                break;
            case 5:
                MessageBox(0,L"Shift+Alt",L"Func Key",MB_ICONINFORMATION|MB_OK);
                break;
            case 6:
                MessageBox(0,L"Shift+Ctrl",L"Func Key",MB_ICONINFORMATION|MB_OK);
                break;
            case 7:
                MessageBox(0,L"Shift+Ctrl+Alt",L"Func Key",MB_ICONINFORMATION|MB_OK);
                break;           
        }
        chkExtKey(keyCode);//擴展鍵
    }
}
參考
[1].wxKeyEvent Class Reference
http://docs.wxwidgets.org/trunk/classwx_key_event.html
[2].wxDev C++ 開始使用它來建立視窗(2)
http://kowala21.blogspot.tw/2012/05/wxdev-c-2.html
[3].Type KeyEvent
http://wxpython.org/docs/api/wx.KeyEvent-class.html

wxDev C++ 使用 winFrame 視窗+測試鍵盤 wxKeyEvent 之1


轉載請註明出處,謝謝。


kowala's home
http://kowala21.blogspot.com

今天就來測試鍵盤,使用 wxDev C++ 來製作 winFrame 視窗,跟 MFC 有一點不一樣,但是都差不多,我們先開一個專案 kbTest,然後為它加入 KeyDown 事件,再來解析按鍵值。

1.我們先開一個專案 kbTest,步驟如下。




 到這裡,就算完成了專案,我們先按綠色三角形來執行看看。



成功執行

接著為它加入 KeyDown 事件,按照下面步驟123。


可以看到 wxDevC++ 幫我們建立了 KeyDown 事件,它改變了兩個檔案 kbTestFrm.h 及kbTestFrm.cpp,來看看哪裡被修改了。

kbTestFrm.h
--------------------------------------------------------------------------------------

 class kbTestFrm : public wxFrame
{
    private:
        DECLARE_EVENT_TABLE();
      
    public:
        kbTestFrm(wxWindow *parent, wxWindowID id = 1, const wxString &title = wxT("kbTest"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = kbTestFrm_STYLE);
        virtual ~kbTestFrm();      
        void kbTestFrmKeyDown(wxKeyEvent& event);
    private:
--------------------------------------------------------------------------------------

這裡新增了事件的宣告


kbTestFrm.cpp
--------------------------------------------------------------------------------------

//Add Custom Events only in the appropriate block.
//Code added in other places will be removed by wxDev-C++
////Event Table Start
BEGIN_EVENT_TABLE(kbTestFrm,wxFrame)
    ////Manual Code Start
    ////Manual Code End
   
    EVT_CLOSE(kbTestFrm::OnClose)
    EVT_KEY_DOWN(kbTestFrm::kbTestFrmKeyDown)
END_EVENT_TABLE()
////Event Table End
...

/*
 * kbTestFrmKeyDown0
 */
void kbTestFrm::kbTestFrmKeyDown(wxKeyEvent& event)
{
    // insert your code here
}
--------------------------------------------------------------------------------------
在 kbTestFrm.cpp 修改了兩個地方,先增加了事件表

BEGIN_EVENT_TABLE(kbTestFrm,wxFrame)
...
 EVT_KEY_DOWN(kbTestFrm::kbTestFrmKeyDown)
END_EVENT_TABLE()

再來是實作這個事件,我們只需在這裡頭填入代碼,做我們的工作,不用擔心事件怎麼寫,只需專注於 wxKeyEvent 處理即可。
 /*
 * kbTestFrmKeyDown0
 */
void kbTestFrm::kbTestFrmKeyDown(wxKeyEvent& event)
{
    // insert your code here
}

下一篇  wxDev C++ 使用 winFrame 視窗+測試鍵盤 wxKeyEvent 之2

2012-07-18

Prince of Persia 波斯王子1 PrinceBot

PrinceBot
這是個簡單的 Bot,功能有自動載入/關閉遊戲,補血,補時間。
自動載入遊戲後,再按一次 Enter ,就會更新時間及血量,必須進到遊戲畫面後,才有數值可以抓取,預設是血10,時間100,執行畫面如下。

 
使用說明:

先確認各路徑是否正確,否則會讀不到遊戲或DOSBox。

模擬器 DOSBox  位於

C:\Program Files\DOSBox-0.74 

如果版本不一樣,請修改路徑為0.74 (同上)

Prince of Persia  位於

C:\Prince

請解壓縮後,自行 copy 過去


遊戲操作:

請參考前文  Prince of Persia 波斯王子1 


有興趣寫程式的參考:

人物的PID: 0x071e0020  >> 這個值每台電腦都不一樣
可以在這裡  0x0074B6B0 讀4個 bytes 取得 pid。

人物的血容量 是在位址 0x07202712 = pid offset 226f2
人物的血量 是在位址 0x072013b8 = pid offset 21398
遊戲時間 是在位址 0x0720169e = pid offset 2167e

修改要先改容量,再改血量,不然沒用。
時間就直接更新就行了。

技術參考:

血容量
00486ED0 - 85 C0  - test eax,eax
00486ED2 - 0F84 FBA20000 - je 004911D3
00486ED8 - 0FB7 14 08   - movzx edx,word ptr [eax+ecx] <<
00486EDC - 0FB7 05 3029B901  - movzx eax,word ptr [LoadDLS+15DC090]
00486EE3 - B9 17000000 - mov ecx,00000017


0047A765 - 85 C0  - test eax,eax
0047A767 - 0F84 6F180100 - je 0048BFDC
0047A76D - 66 89 1C 08   - mov [eax+ecx],bx <<
0047A771 - 8B 35 C8B07400  - mov esi,[LoadDLS+194828]
0047A777 - E9 14D6FFFF - jmp 00477D90

時間
0049D32B - 85 C0  - test eax,eax
0049D32D - 0F84 EF000000 - je 0049D422
0049D333 - 66 89 0C 18   - mov [eax+ebx],cx <<
0049D337 - 8B 35 C8B07400  - mov esi,[LoadDLS+194828]
0049D33D - BB 1D000000 - mov ebx,0000001D

人物-x
004775BB - 85 C0  - test eax,eax
004775BD - 0F84 76040000 - je 00477A39
004775C3 - 66 89 0C 18   - mov [eax+ebx],cx <<
004775C7 - 01 7C 24 3C  - add [esp+3C],edi
004775CB - 8B 5C 24 38  - mov ebx,[esp+38]

人物-y
004775BB - 85 C0  - test eax,eax
004775BD - 0F84 76040000 - je 00477A39
004775C3 - 66 89 0C 18   - mov [eax+ebx],cx <<
004775C7 - 01 7C 24 3C  - add [esp+3C],edi
004775CB - 8B 5C 24 38  - mov ebx,[esp+38]

遊戲座標圖


外掛下載: PrinceBot20130218.rar 231kb
(內含 PrinceBot 說明及前一版載入器 PrinceLoader ,PrinceLoader 只載入,不修改,原汁原味的玩),這是 dropbox 的硬碟,不錯用。

2012-07-11

Prince of Persia 波斯王子1 載入器 PrinceLoader

由於這款古老的遊戲,是16位元的,在 Win7 下無法順利執行,必須透過模擬軟體來執行,模擬軟體又以 DOSBox 最為簡便,雖然簡便,但仍然是需要手動打字輸入,步驟如下:

1.首先必須建立連結實體目錄

mount p c:\prince

2.切換目錄

p:

3.執行

prince

如下圖所示


以上是手動輸入,每次執行遊戲都要打很多字,這樣會減低玩遊戲的樂趣 :-\
於是就寫了這隻載入器 PrinceLoader,讓電腦來幫忙打字,及開啟,關閉遊戲。
PrinceLoader 使用時,須先確認各路徑是否正確,否則會讀不到遊戲或DOSBox。

DOSBox  位於
C:\Program Files\DOSBox-0.74
由於路徑是寫死在載入器 PrinceLoader裡頭,若 DosBox 版本不同時,請改成跟這裡一樣。

Prince of Persia  位於
C:\Prince

這樣就可以順利執行了 ^^

下載 PrinceLoader

https://www.dropbox.com/s/7xjftierbu7t14p/PrinceLoader.rar




這裡是遊戲進行的影片,到最後結局,當然,也是需要靠一些電腦的輔助(autoBot),這也可以彌補一下 Prince of Persia 1 無法存檔的缺點。


2012-07-07

wxDev C++ ,如何從鍵盤參數 lParam 取出 scan code。

想使用鍵盤來模擬按鍵,如果有使用到鍵盤事件,這個參數 lParam 就有必要去了解一下。

依據 MSDN 說明如下:
wParam [in]    Type: WPARAM
    The virtual-key code of the key that generated the keystroke message.

lParam [in]    Type: LPARAM
    The repeat count, scan code, extended-key flag, context code,
    previous key-state flag, and transition-state flag. For more information about
    the lParam parameter, see Keystroke Message Flags. The following table
    describes the bits of this value.

    Bits    Description
    0-15    The repeat count. The value is the number of times the keystroke is repeated as a result of the user's holding down the key.
    16-23    The scan code. The value depends on the OEM.
    24    Indicates whether the key is an extended key, such as a function key or a key on the numeric keypad. The value is 1 if the key is an extended key; otherwise, it is 0.
    25-28    Reserved.
    29    The context code. The value is 1 if the ALT key is down; otherwise, it is 0.
    30    The previous key state. The value is 1 if the key is down before the message is sent; it is 0 if the key is up.
    31    The transition state. The value is 0 if the key is being pressed and 1 if it is being released.


位元定義圖[1]


lParam 它是長度 DWORD 的數值,各位元有其代表意義[2]。現在我們關心的是位元 16-23,依定義知道它是代表 scan code,下面示範如何取出它。



ex: 取值'a' = 0xc01e0001,16-23 The scan code.(0x1e=30)

1100 0000 0001 1110 0000 0000 0000 0001 = 0xc01e0001

先與 0xFF0000 AND
0000 0000 1111 1111 0000 0000 0000 0000 = 0x00FF0000
1100 0000 0001 1110 0000 0000 0000 0001 = 0xc01e0001

結果
0000 0000 0001 1110 0000 0000 0000 0000

再右旋16次可得
0000 0000 0000 0000 0000 0000 0001 1010

取 scan code
scankey=(((DWORD)(lParam) & 0xFF0000) >> 16);

運算後
scankey= 0000 0000 0000 0000 0000 0000 0001 1010

WORD xx=(WORD)scankey;
xx = 0000 0000 0001 1110
HIBYTE(xx)   = 0x00 = 0000 0000
LOBYTE(xx) = 0x1e = 0001 1110

資料長度
BYTE  =  8 bits
WORD  = 16 bits
DWORD = 32 bits

參考資料
[1]. About Keyboard Input
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646267%28v=vs.85%29.aspx
[2]. WM_CHAR
http://msdn.microsoft.com/en-us/library/aa453873