static LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam)
{
    if (code >= 0)
    {
        switch(code)
        {
        case HC_ACTION:
        {
            LPMSG msg = (LPMSG)lParam;

            //for (HandleList::iterator it = s_windows->begin(); it != s_windows->end(); ++it)
            //{
            //	RegisterTouchWindow(*it, TOUCH_FLAGS);
            //}

            /*
            if(keyboardHandle != NULL)
            {
            	FixWindowForTouch(keyboardHandle);
            }
            */

            // to register all windows as touch-capable
            /*
            if (msg->hwnd != 0)
            {
            	bool found = false;
            	for (int i = 0; i < registeredWindows.size(); ++i)
            		if (registeredWindows[i] == msg->hwnd)
            		{
            			found = true;
            			break;
            		}
            	if (!found)
            	{
            		RegisterTouchWindow(msg->hwnd, TOUCH_FLAGS);
            		char buf[10240];
            		sprintf(buf, "registering %d; adr = %x\n", msg->hwnd, windowsToRegister);
            		trace(buf);
            		registeredWindows.push_back(msg->hwnd);
            	}
            }
            */

            /*
            while (windowsToRegister.size() > 0)
            {
            	RegisterTouchWindow(windowsToRegister.back(), TOUCH_FLAGS);

            	// register also all child windows to receive touch
            	long userData = 0;
            	EnumChildWindows(windowsToRegister.back(), enumChildWindowsCallback, userData);

            	windowsToRegister.pop_back();
            }
            */

            /*
            		char buf[10240];
            		sprintf(buf, "searching %d, size = %d adr = %x\n", msg->hwnd, wtrCount, windowsToRegister);
            		trace(buf);
            */

#if 0
            int pos = -1;
            for (int i = 0; i < /*windowsToRegister.size()*/wtrCount; ++i)
            {
                /*
                	char buf[10240];
                	sprintf(buf, "vector[%d] = %d\n", i, windowsToRegister[i]);
                	trace(buf);
                	*/
                if (windowsToRegister[i] == msg->hwnd)
                {
                    /*
                    char buf[10240];
                    sprintf(buf, "is found in vector at pos: %d\n", i);
                    trace(buf);
                    */
                    pos = i;
                }
            }
            //std::vector<HWND>::iterator pos = std::find(windowsToRegister.begin(), windowsToRegister.end(), msg->hwnd);
            //if (pos != windowsToRegister.end())
            if (pos >= 0)
            {
                RegisterTouchWindow(msg->hwnd, TOUCH_FLAGS);
                //windowsToRegister.erase(pos);
                for (int i = pos + 1; i < wtrCount; ++i)
                    windowsToRegister[i - 1] = windowsToRegister[i];
                --wtrCount;
                /*
                	char buf[10240];
                	sprintf(buf, "registering known window %d\n", msg->hwnd);
                	trace(buf);
                */
                FILE* trace = fopen("c:\\patrice\\patrice.txt", "a");
                if (trace != 0)
                {
                    TCHAR windowTitle[1024];
                    GetWindowText(msg->hwnd, windowTitle, 1024);
                    TCHAR className[1024];
                    GetClassName(msg->hwnd, className, 1024);
                    fwprintf(trace, L"%s - %s\n", windowTitle, className);
                    fclose(trace);
                }
            }
#endif

            //if (!IsTouchWindow(msg->hwnd, NULL))
            //{
            //	if (RegisterTouchWindow(msg->hwnd, TOUCH_FLAGS))
            //	{
            //		touchRegistered = true;
            //	}
            //}

            //TMP
            //FixWindowForTouch(msg->hwnd);
            //END TMP

            // a window is candidate for registering for touch (or back to gesture)
            // if its class is TmioEngineWin or Internet Explorer_Server
            // and the window or one of its ancertors has been declared with TouchEnable or GestureEnable

            HWND hwnd = msg->hwnd;
            TCHAR className[1024];

            if (hwnd != 0 && GetClassName(hwnd, className, 1024))
            {
                if (wcsncmp(className, L"TmioEngineWin", 1024) == 0
                        || wcsncmp(className, L"Internet Explorer_Server", 1024) == 0)
                {
                    while (hwnd != 0)	// loop thru ancestors
                    {
                        int i = findWindowHandle(hwnd);
                        if (i != -1)
                        {
                            if (isTouch[i])
                                FixWindowForTouch(msg->hwnd);
                            else
                                FixWindowForGesture(msg->hwnd);
                            break;
                        }
                        hwnd = GetParent(hwnd);
                    }
                }
            }

            switch(msg->message)
            {
            case WM_TOUCH:
            {
                // WM_TOUCH message can contain several messages from different contacts
                // packed together.
                // Message parameters need to be decoded:
                unsigned int numInputs = (unsigned int) msg->wParam; // Number of actual per-contact messages
                TOUCHINPUT* ti = new TOUCHINPUT[numInputs]; // Allocate the storage for the parameters of the per-contact messages
                if (ti == NULL)
                {
                    break;
                }

                //int maxTouchInputs = GetSystemMetrics(SM_MAXIMUMTOUCHES);

                // Unpack message parameters into the array of TOUCHINPUT structures, each
                // representing a message for one single contact.
                if (GetTouchInputInfo((HTOUCHINPUT)msg->lParam, numInputs, ti, sizeof(TOUCHINPUT)))
                {
                    // For each contact, dispatch the message to the appropriate message
                    // handler.

                    for (unsigned int i = 0; i < numInputs; ++i)
                    {
                        if (ti[i].dwFlags & TOUCHEVENTF_DOWN)
                        {
                            OnTouchDownHandler(msg->hwnd, ti[i]);
                        }
                        else if (ti[i].dwFlags & TOUCHEVENTF_MOVE)
                        {
                            OnTouchMoveHandler(msg->hwnd, ti[i]);
                        }
                        else if (ti[i].dwFlags & TOUCHEVENTF_UP)
                        {
                            OnTouchUpHandler(msg->hwnd, ti[i]);
                        }
                    }
                }

                CloseTouchInputHandle((HTOUCHINPUT)lParam);
                delete [] ti;
            }
            break;

            /*
            				case WM_GESTURE:
            					{
            						trace("WM_GESTURE\n");
            					}
            					break;
            */

            case WM_QUIT:
                UninitializeTouch();
                break;

            case WM_APP + 5:
                int arg0 = LOWORD(msg->wParam);
                int arg1 = HIWORD(msg->wParam);
                int arg2 = LOWORD(msg->lParam);
                int arg3 = HIWORD(msg->lParam);

                if(arg0 == EX_DESTROYWINDOW)
                {
                    DestroyWindow((HWND)msg->lParam);
                }
                break;
            }
            break;
        }
        }
    }

    return CallNextHookEx(0, code, wParam, lParam);
}
// Processes messages for main Window
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    PTOUCHINPUT pInputs;
    TOUCHINPUT tInput;
    HTOUCHINPUT hInput;
    int iNumContacts;
    POINT ptInputs;
    PAINTSTRUCT ps;

    // Handle each type of inData and based on which event we handle continue processing
    // the inData for manipulations by calling the ComTouchDriver
    
    switch (msg)
    {
    case WM_TOUCH:

        iNumContacts = LOWORD(wParam);
        hInput = (HTOUCHINPUT)lParam;

        pInputs = new (std::nothrow) TOUCHINPUT[iNumContacts];
       
        // Get each touch input info and feed each TOUCHINPUT into the process input handler

        if(pInputs != NULL)
        {
            if(GetTouchInputInfo(hInput, iNumContacts, pInputs, sizeof(TOUCHINPUT)))
            {
               for(int i = 0; i < iNumContacts; i++)
               {
                   // Bring touch input info into client coordinates

                   ptInputs.x = pInputs[i].x/100;	
                   ptInputs.y = pInputs[i].y/100;
                   ScreenToClient(g_hWnd, &ptInputs);
                   pInputs[i].x = ptInputs.x;
                   pInputs[i].y = ptInputs.y;
                   g_ctDriver->ProcessInputEvent(&pInputs[i]);
               }
               g_ctDriver->ProcessChanges();
            }
        }
        
        delete [] pInputs;
        CloseTouchInputHandle(hInput);
        break;
    
    // For each Mouse event build a TOUCHINPUT struct and feed into the process input handler

    case WM_LBUTTONDOWN:

        FillInputData(&tInput, MOUSE_CURSOR_ID, TOUCHEVENTF_DOWN, (DWORD)GetMessageTime(),LOWORD(lParam),HIWORD(lParam));
        g_ctDriver->ProcessInputEvent(&tInput);        
        break;

    case WM_MOUSEMOVE:

        if(LOWORD(wParam) == MK_LBUTTON)
        {
            FillInputData(&tInput, MOUSE_CURSOR_ID, TOUCHEVENTF_MOVE, (DWORD)GetMessageTime(),LOWORD(lParam), HIWORD(lParam));
            g_ctDriver->ProcessInputEvent(&tInput);      
            g_ctDriver->ProcessChanges();
        }
      
        break;

    case WM_LBUTTONUP:

        FillInputData(&tInput, MOUSE_CURSOR_ID, TOUCHEVENTF_UP, (DWORD)GetMessageTime(),LOWORD(lParam), HIWORD(lParam));
        g_ctDriver->ProcessInputEvent(&tInput);
        
        break;

    case WM_DESTROY:
        
        if(g_ctDriver)
        {
            delete g_ctDriver;
        }
        PostQuitMessage(0);
        return 1;

    case WM_SIZE:

        g_iCWidth = LOWORD(lParam);
        g_iCHeight = HIWORD(lParam);
        break;

    case WM_PAINT:
        
        BeginPaint(g_hWnd, &ps);
        g_ctDriver->RenderInitialState(g_iCWidth, g_iCHeight);
        EndPaint(g_hWnd, &ps);
        break;

    case WM_TIMER:
        g_ctDriver->ProcessChanges();
        break;
      
    default:
        return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    return 0;
}