Monday, March 10, 2008

How to make a cellphone like keybaord?

Cellphone like keyboard: press "1" to input "a,b,c" depends how many time you hit the same key in a timely manner.

Step 1: SetWindowsHookEx

#include
#include
#include

#define WH_KEYBOARD_LL 20

typedef LRESULT (CALLBACK* HOOKPROC)(int code, WPARAM wParam, LPARAM lParam);
typedef HHOOK (WINAPI *_SetWindowsHookExW)(int, HOOKPROC, HINSTANCE, DWORD);
typedef LRESULT (WINAPI *_CallNextHookEx)(HHOOK, int, WPARAM, LPARAM);
typedef LRESULT (WINAPI *_UnhookWindowsHookEx)(HHOOK);

typedef struct {
DWORD vkCode;
DWORD scanCode;
DWORD flags;
DWORD time;
ULONG_PTR dwExtraInfo;
} KBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;

static _SetWindowsHookExW SetWindowsHookEx;
static _UnhookWindowsHookEx UnhookWindowsHookEx;
static _CallNextHookEx CallNextHookEx;

HINSTANCE gHook = NULL;
HHOOK gInst = NULL;

BOOL activate(HINSTANCE, HOOKPROC);
BOOL deactivate();
LRESULT CALLBACK hook(int nCode, WPARAM wParam, LPARAM lParam);

//
// activate() -
//
BOOL activate(HINSTANCE hInst, HOOKPROC hProc)
{
SetWindowsHookEx = NULL;
CallNextHookEx = NULL;
UnhookWindowsHookEx = NULL;

gHook = LoadLibrary(_T("coredll.dll"));
if(gHook == NULL) {
MessageBox(NULL, L"Failed to LoadLibrary!", L"",MB_OK);
return(false);
} else {

SetWindowsHookEx = (_SetWindowsHookExW)GetProcAddress(
gHook, _T("SetWindowsHookExW"));
if(SetWindowsHookEx == NULL) {
MessageBox(NULL, L"Failed to SetWindowsHookEx!", L"",MB_OK);
return(false);
} else {
gInst = SetWindowsHookEx(WH_KEYBOARD_LL, hProc, hInst, 0);
CString aa;
aa.Format(L"Error=%d", GetLastError());
if(gInst == NULL) {
MessageBox(NULL, aa, L"",MB_OK);
return false;
}
}

CallNextHookEx = (_CallNextHookEx)GetProcAddress(
gHook, _T("CallNextHookEx"));
if(CallNextHookEx == NULL) {
MessageBox(NULL, L"CallNextHookEx == NULL", L"",MB_OK);
return false;
}

UnhookWindowsHookEx = (_UnhookWindowsHookEx)GetProcAddress(
gHook, _T("UnhookWindowsHookEx"));
if(UnhookWindowsHookEx == NULL) {
MessageBox(NULL, L"UnhookWindowsHookEx == NULL", L"",MB_OK);
return(false);
}
}
return(true);
}


//
// deactivate() -
//
BOOL deactivate()
{
if(gInst != NULL) {
UnhookWindowsHookEx(gInst);
gInst = NULL;
}
if(gHook != NULL) {
FreeLibrary(gHook);
gHook = NULL;
}
return(true);
}

//
// hook() -
//
LRESULT CALLBACK
hook(int nCode, WPARAM wParam, LPARAM lParam)
{
// Do something interesting here
if(((((KBDLLHOOKSTRUCT*)lParam)->vkCode) == VK_LEFT) ||
((((KBDLLHOOKSTRUCT*)lParam)->vkCode) == VK_RIGHT))
{
//Generate the keyboard press event of the mapped key

keybd_event(VK_UP, 0, 0, 0);

//release the mapped key

keybd_event(VK_UP, 0, KEYEVENTF_KEYUP, 0);
}
return CallNextHookEx(gInst, nCode, wParam, lParam);
}

Step 2: For window CE, there is only one SetWindowsHookEx can be used. So kill other application who is using it.

//
// TODO: Add extra initialization here
//Kill XXXManger in order to test!
HWND hXXXMgrCurrent = ::FindWindow(L"XXXMGR", L"XXXManager");
if(hLedMgrCurrent)
{
MessageBox(L"Warning: XXXMGR currently running. It will be killed in this testing!",MB_OK);
// During development, we want to kill the currently running program so we can launch a new one
::SendMessage(hXXXMgrCurrent,WM_DESTROY,0,0); //in that manager, accept this mesage to close.
Sleep(10); // allow the previous instance to clean up
}
//
Step 3: call active
HINSTANCE m_hInstance; (header file)
m_hInstance = this->m_hInstance; (Constructor)
if (!activate(m_hInstance, hook)) {
MessageBox(L"Failed to active!", L"",MB_OK);
}else
{


Step 4: Intercept the message and create timer to count when to input as loop.

void CALLBACK EXPORT processKeyData(
HWND hWnd, // handle of CWnd that called SetTimer
UINT nMsg, // WM_TIMER
UINT nIDEvent, // timer identification
DWORD dwTime // system time
);

UINT IDT_KEY_TIMER,timerValue;
int iACode1 = 0;
bool bAlive1 = false;
//
// hook() -
//
LRESULT CALLBACK
hook(int nCode, WPARAM wParam, LPARAM lParam)
{
switch((((KBDLLHOOKSTRUCT*)lParam)->vkCode))
{
case 0x32:
if(!bAlive1)
{
timerValue = ::SetTimer(NULL, IDT_KEY_TIMER, 2000, (TIMERPROC) processKeyData);
bAlive1 = true;
}
iACode1++;
return 1; //Must return to avoid original input on screen
}

return CallNextHookEx(gInst, nCode, wParam, lParam);
}

void CALLBACK processKeyData(
HWND hWnd, // handle of CWnd that called SetTimer
UINT nMsg, // WM_TIMER
UINT nIDEvent, // timer identification
DWORD dwTime // system time
)
{
if(bAlive1)
{
switch(iACode1%3)
{
case 2: //soft panel works great! but JETT hardkey reverse it with 'b'????
keybd_event(0x41,0x9e,0 , 0);
keybd_event(0x41,0x9e, KEYEVENTF_KEYUP, 0);
bAlive1 = false;
iACode1 = 0;
::KillTimer(NULL, timerValue);
break;
case 1:
keybd_event(0x42,0xb0,0 , 0);
keybd_event(0x42,0xb0, KEYEVENTF_KEYUP, 0);
bAlive1 = false;
iACode1 = 0;
::KillTimer(NULL, timerValue);
break;
case 0:
keybd_event(0x43,0xae,0 , 0);
keybd_event(0x43,0xae, KEYEVENTF_KEYUP, 0);
bAlive1 = false;
iACode1 = 0;
::KillTimer(NULL, timerValue);
break;
default:
break;
}
}
}



No comments: