© 2004 Microsoft Corporation. All rights reserved.

Figure 1 Function Definitions in CursorShape.cpp
#define IDC_CUSTOM_POINTER 104

#include <windows.h>

// Table of the cursors supported by this application. Defined in 
// CursorShape.cpp
LPSTR pSupportedCursors [];

// Cursor Verification Functions 
int IsCursor(HINSTANCE hInstance, LPCTSTR lpCursorName);
int CurrentCursor(int &cCursorNum, HINSTANCE hInstance);
int WaitForCursor(HINSTANCE hInstance, bool bWaitForGivenCursor, 
    int SleepTime, int TimeOut, int cCursorsNumber,...);

// Internal functions
int CompareBitmaps(HBITMAP hbmpCurrent, HBITMAP hbmpRequested);
bool GetCursorBitmapBits(HDC hdcScreen, HBITMAP hSourceBitmap, 
                         HGLOBAL &hMemImage, DWORD &cMemImage);
int GetDIBItsCursor(HDC hDC, HBITMAP hBmp, int nWidth, int nHeight, 
    DWORD &nSizeImage, int nBitsPerPixel, LPVOID lpGetBits, 
    LPBITMAPINFO lpBmpinfo);
DWORD GetBmpInfoSizeCursor(int nBitsPerPixel);
Figure 2 Monitoring WinEvents to Obtain Cursor Information
///////////////////////////////////////////////////////////////////////////
//STEP 1 - Demonstrates how CurrentCursor() can be used to recognize a 
//user-defined cursor 
///////////////////////////////////////////////////////////////////////////

// Get the current position of the cursor and the window that the 
// cursor belongs to
GetCursorPos(&pt);
hWnd = WindowFromPoint(pt);

// Get the thread ID for the window that the cursor belongs to
dwThreadID = GetWindowThreadProcessId(hWnd, NULL);

// Get the thread ID for the current thread
dwCurrentThreadID = GetCurrentThreadId();

// Since this application doesn't support any windows, assume that the 
// current thread is not the thread that created the window that the cursor 
// belongs to. If this is not the case don't call AttachThreadInput

// Attach to the thread that created the window that the cursor belongs to
if (AttachThreadInput(dwCurrentThreadID, dwThreadID, TRUE)) 
{
        // Set the the cursor
        hCursor = SetCursor(LoadCursor(hInstance, pSupportedCursors[15]));

        // Verify what is the global cursor.  It must be what we just set
        retVal = CurrentCursor(cCursorNum, hInstance);

        if(retVal == 1 && cCursorNum == 15) 
            MessageBox(NULL, "The requested cursor has been recognized",
                       "CurrentCursor() function has been called", MB_OK);
        else
            MessageBox(NULL, "ERROR: The requested cursor has not been 
                       recognized", "CurrentCursor() function has been 
                       called", MB_OK);

        // Detach from the thread that created the window that the cursor 
        // belongs to
        AttachThreadInput(dwCurrentThreadID, dwThreadID, FALSE);

}
///////////////////////////////////////////////////////////////////////////   //STEP 2 - Demonstrates how WaitForCursor() can be used to wait until a 
//system cursor appears
///////////////////////////////////////////////////////////////////////////

MessageBox(NULL, "Application will be waiting for one of the IDC_SIZENS 
           or IDC_SIZEWE cursors to show up.\nClick OK to start the test.", 
           "WaitForCursor() function will be called", MB_OK);

retVal = WaitForCursor(NULL, true, 500, 120000, 2, IDC_SIZENS, IDC_SIZEWE);

if(retVal == 1)
    MessageBox(NULL, "One of the requested cursors has been recognized", 
               "WaitForCursor() function has been called", MB_OK);
else if(retVal == 0)
    MessageBox(NULL, "ERROR: WaitForCursor() function reported that one 
               of the requested cursors has disappeared", "WaitForCursor() 
               function has been called", MB_OK);
else
    MessageBox(NULL, "ERROR: WaitForCursor() function failed", 
               "WaitForCursor() function has been called", MB_OK);

///////////////////////////////////////////////////////////////////////////    //STEP 3 - Demonstrates how the WinEvent approach can be used to wait until 
// a system cursor appears
///////////////////////////////////////////////////////////////////////////

MessageBox(NULL, "Application will be waiting for the \"NWSE size\" 
           cursor to show up.\nClick OK to start the test.", "WinEven 
           approach will be used", MB_OK);

HWINEVENTHOOK  hEventHook = NULL;
MSG msg;

// Initialize the COM library 
CoInitialize(NULL);

// Set WinEvent hook
hEventHook = SetWinEventHook(
        EVENT_MIN,
        EVENT_MAX,         //end event id
            NULL,          //always NULL for outprocess hook
            WinEventProc,
            0,             //hooked into
            0,             //idThread, //hooked into
            WINEVENT_SKIPOWNPROCESS | WINEVENT_OUTOFCONTEXT);     //always 
                                              // the same for outproc hook

    while (GetMessage (&msg, NULL, 0, 0) )
    {
        if(msg.message == WM_CURSOR_FOUND)
        {
            MessageBox(NULL, "The requested cursors has been recognized", 
                       "That's all folks", MB_OK);

            //WinEvent WinEvent
            UnhookWinEvent(hEventHook);

            // Close the COM library 
            CoUninitialize();

            return 1;
        }

        TranslateMessage (&msg);
        DispatchMessage (&msg);
    }
// Close the COM library if GetMessage fails
    CoUninitialize();

    return 0;
}

// —————————————————————————————————————
//
//  WinEventProc()
//
// —————————————————————————————————————
void CALLBACK WinEventProc
(
    HWINEVENTHOOK  hEvent,
    DWORD   event,
    HWND    hwndMsg,
    LONG    idObject,
    LONG    idChild,
    DWORD   idThread,
    DWORD   dwmsEventTime
)
{
    // If idObject is not OBJID_CURSOR we return

    if(idObject != OBJID_CURSOR)

        return;

    char bufferName[256];
    IAccessible *pacc=NULL;
    VARIANT varChild;
    VariantInit(&varChild);

    // Get a pointer to IAccessible and a child ID for the event
    HRESULT hr= AccessibleObjectFromEvent(hwndMsg, idObject, idChild,
                                          &pacc, &varChild);

    if(!SUCCEEDED(hr))
    {
        VariantClear(&varChild);
        return;
    }

    VARIANT varRetVal;

    // Get the role and verify that the event corresponds to the cursor
    hr = pacc->get_accRole(varChild, &varRetVal);
    
    if (hr != S_OK || varRetVal.vt != VT_I4 || varRetVal.lVal 
        != ROLE_SYSTEM_CURSOR)
    {
        pacc->Release();
        return;
    }

    // Get the name of the cursor
    BSTR bstrName;
    hr = pacc->get_accName(varChild, &bstrName);
    
    if (hr == S_OK && bstrName)
    {
        WideCharToMultiByte(CP_ACP, 0, bstrName, -1, bufferName, 
                            sizeof(bufferName), NULL, NULL);
        SysFreeString(bstrName);
    }
    else
        lstrcpyn(bufferName, "unknown name", sizeof(bufferName));

    // Verify if the name is the requested one
    // Note that this comparison will not work on localized builds
    if(strstr(bufferName, "NWSE size"))
        PostThreadMessage(GetCurrentThreadId(), WM_CURSOR_FOUND, 0, 0);

    pacc->Release();

    return;
}