Prabhu Kumar

a tech twaddler..

Archive for September, 2009

CloseHandle(CreateThread(…));

without comments

I was browsing through some code when I came across a line of code which said:



CloseHandle( CreateThread(NULL, 0, PeculiarThreadProc, NULL, 0, NULL) );



And I thought, wait a minute, is he closing the handle to the thread immediately after its created. I pinged my TL and he said the thread will continue to run until it terminates. I thought I had read in documents that a thread will terminate when the last handle to it was closed and here it was the last and only handle to the thread. A closer look at the documentation made it clear,



"The thread object remains in the system until the thread has terminated and all handles to it are closed through a call to Closehandle."



I had missed the ‘and’. Anyways, to kill my curiosity for good I went ahead and tested the following code,



int WINAPI WinMain(HINSTANCE hInstance,

                   HINSTANCE hPrevInstance,

                   LPTSTR    lpCmdLine,

                   int       nCmdShow)

{

    CloseHandle( CreateThread(NULL, 0, PeculiarThreadProc, NULL, 0, NULL));


     while(1)

     {

          Sleep(1000);

     }



    return 0;

}



DWORD WINAPI PeculiarThreadProc(void *lPtr)

{

    DWORD tick;

    while(1)

    {

        tick = GetTickCount();

        printf("Tick count:%u\r\n", tick);

        Sleep(1000);       

    }

}



And as expected the thread kept running in all its glory.

Suppose we modify our program a little as below,

int WINAPI WinMain(HINSTANCE hInstance,


                   HINSTANCE hPrevInstance,

                   LPTSTR    lpCmdLine,

                   int       nCmdShow)

{

    CreateThread(NULL, 0, PeculiarThreadProc, NULL, 0, NULL);

    
     while(1)

     {

          //do nothing

          Sleep(1000);

     }



    return 0;

}



DWORD WINAPI PeculiarThreadProc(void *lPtr)

{

        printf("PeculiarThreadProc");

        Sleep(100);       

}


So in this case, the thread terminates but the handle to the thread is not closed. So does that mean that the thread object remains in the system? The answer is yes, and the thread object will exist in signaled state. And of course, if the main thread itself exits then all hope is lost (;

Written by Prabhu Kumar

September 12th, 2009 at 11:19 pm

Posted in Uncategorized

Applications: Creating a windowed DirectDraw application

with 2 comments

In this post we’ll learn how to create a windowed directdraw application for windows mobile. A windowed application is not much different from a regular full-screen application but you have to be a little careful because your application has to co-exist with GDI, peacefully. Note that I have used bits of code from Joel’s code project article. Here is the code I call from WinMain():



    //Create the DirectDraw object the primary and auxillary surfaces

    if (InitDirectDraw(g_hWnd))

    {

        if (!CreateDirectDrawSurfaces(g_hWnd))

        {

            printf("CreateDirectDrawSurfaces failed!\r\n");

            FreeDirectDrawResources();

            return FALSE;

        }

    }

    else

    {

        printf("InitDirectDraw failed!\r\n");

        FreeDirectDrawResources();

        return FALSE;

    }



    printf("Checkpoint 1: InitDirectDraw and CreateDirectDrawSurfaces succeeded\r\n");



    //Initialize the surfaces with the image

    if (!InitSurfaces())

    {

        printf("InitSurfaces failed!\r\n");

        return FALSE;

    }

We will see what each one of the functions does, one by one.



BOOL InitDirectDraw(HWND hWnd)

{

    HRESULT result;



    result = DirectDrawCreate(NULL, &g_ddraw, NULL);

    if(SUCCEEDED(result))

    {

        g_ddraw->SetCooperativeLevel(hWnd, DDSCL_NORMAL);

        return TRUE;

    }



    printf("DirectDrawCreate failed!\r\n");

    return FALSE;



}

InitDirectDraw() is simple. The only difference here is the co-operative level, we use DDSCL_NORMAL unlike DDSCL_FULLSCREEN that we used before. The CreateDirectDrawSurfaces() function is below:



BOOL CreateDirectDrawSurfaces(HWND hWnd)

{

    DDSURFACEDESC ddsd;

    HRESULT hr;

    RECT rect = {0,};



    ZeroMemory(&ddsd, sizeof(DDSURFACEDESC));



    ddsd.dwSize = sizeof(DDSURFACEDESC);

    ddsd.dwFlags = DDSD_CAPS;

    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;



    //create the primary surface and set the clipper

    if ( (g_ddraw->CreateSurface(&ddsd, &g_primarySurface, NULL)) == DD_OK)

    {

        if ( (g_ddraw->CreateClipper(0, &g_primaryClipper, NULL)) == DD_OK)

        {

            if ( (g_primaryClipper->SetHWnd(0, hWnd)) != DD_OK)

            {

                printf("SetHWnd on primary clip failed hr=0x%x\r\n", hr);

            }

            if ( (g_primarySurface->SetClipper(g_primaryClipper)) != DD_OK)

            {

                printf("SetClipper failed hr=0x%x\r\n", hr);

            }

        }

        else

        {

            printf("CreateClipper failed hr=0x%x\r\n", hr);

            return FALSE;

        }

    }

    else

    {

        printf("CreateSurface failed hr=0x%x\r\n", hr);

        return FALSE;

    }



    //get the size of the client area (this will be used for backbuffer)

    GetClientRect(hWnd, &rect);



    ZeroMemory(&ddsd, sizeof(DDSURFACEDESC));



    //create the back buffer

    ddsd.dwSize = sizeof(DDSURFACEDESC);

    ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT;

    ddsd.dwWidth = rect.right – rect.left;

    ddsd.dwHeight = rect.bottom – rect.top;



    printf("screen width:%d,  height:%d\r\n", ddsd.dwWidth, ddsd.dwHeight);



    if ( (g_ddraw->CreateSurface(&ddsd, &g_backBuffer, NULL)) != DD_OK)

    {

        printf("CreateSurface on back buffer failed hr=0x%x\r\n", hr);

        return FALSE;

    }



    ZeroMemory(&ddsd, sizeof(DDSURFACEDESC));



    //create a surface to hold the marbel’s image

    ddsd.dwSize = sizeof(DDSURFACEDESC);

    ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT;

    ddsd.dwWidth = 48; //BMP’s width

    ddsd.dwHeight = 48; //BMP’s height



    if ( (hr = g_ddraw->CreateSurface(&ddsd, &g_marbleSurface, NULL)) != DD_OK)

    {

        printf("CreateSurface marble surface failed hr=0x%x\r\n", hr);

        return FALSE;

    }



    return TRUE;

}

We create three surfaces here, a primary surface, a back buffer and an additional surface to hold our marble’s image. Another thing you’ll note is the presence of a DirectDraw Clipper object. The clipper object is used to manage clip lists. Basically, it helps to contain the drawings of our application within the client area of the window. We create the clipper using CreateClipper() method on the directdraw object. Next, we call SetHWnd() on the clipper object and pass in the handle to our main window. The clipper object will use this handle to find out the client area of the window and determine what part to clip. We set the clipper on the primary surface by using SetClipper() method of the directdraw surface object.

 

We create the back buffer next. You’ll see that we use the client area co-ordinates of the main window to decide the width and height of the back buffer. I did not do this initially and used 240 for width and 320 for height. This led to a problem, the image on the screen appeared skewed. The problem was that while Blt’ing, the destination rect on the primary surface was the size of the client area of the main window, which is a little smaller than 240×320 to make way for the task bar and the menu bar. When the back buffer, whose size is 240×320, was Blt onto a smaller surface the image was shrunk to fit. So the image appeared, well, shrunk.



BOOL InitSurfaces()

{

    HBITMAP hbm;



    //load the bitmap resource

    hbm = DDGetBitmapHandle(g_hInst, szMarbleBitmap);



    if (hbm == NULL)

    {

        printf("DDGetBitmapHandle failed\r\n");

        return FALSE;

    }



    DDCopyBitmap(g_marbleSurface, hbm, 0, 0, 48, 48);



    DeleteObject(hbm);



    return TRUE;

}

InitSurfaces() initializes the marble surface with the marble’s image. The DD*() function you see above are from ddutil.cpp which come with the SDK samples. Make sure that the null check "if (hbm == NULL)" isn’t written as "if(hbm = NULL)", that’ll save you a good hour of debugging (;

 

Next up are UpdateFrame() and DisplayFrame() functions. UpdateFrame() updates the back buffer by Blt()’ing the marble surface on it and DisplayFrame() displays the drawing by Blt()’ing the back buffer onto the primary surface.



void DisplayFrame()

{

    HRESULT hr;

    POINT p;

    RECT destRect;



    p.x = p.y = 0;



    ClientToScreen(g_hWnd, &p);

    GetClientRect(g_hWnd, &destRect);



    OffsetRect(&destRect, p.x, p.y);



    //blt the back buffer on the primary surface

    while (TRUE)

    {

        hr = g_primarySurface->Blt(&destRect, g_backBuffer, NULL, 0, NULL);



        if (hr == DD_OK)

            break;



        if (hr != DDERR_WASSTILLDRAWING)

            break;

    }

}

Again, we use ClientToScreen(), GetClientRect() and OffsetRect() functions to determine the position of the destination rectangle on the primary surface.

 

Another important thing is to prevent your application from drawing when it’s not supposed to, for e.g. when it doesn’t have the focus. So we need to maintain a state variable to determine whether we have focus or not and draw only when we our application has the focus. The following code is under the WndProc() function:



        case WM_ACTIVATE:

            SHHandleWMActivate(hWnd, wParam, lParam, &s_sai, FALSE);

            switch(LOWORD(wParam))

            {

                case WA_ACTIVE:

                case WA_CLICKACTIVE:

                    g_bHasFocus = TRUE;   

                    break;



                case WA_INACTIVE:

                    g_bHasFocus = FALSE;

                    break;



                default:

                    break;

            }

            break;

Pretty straight forward. Also,



        case WM_CANCELMODE:

            g_bHasFocus = FALSE;

            DisplayTextOnScreen(TEXT("Tap here to continue.."));

            //DisplayFrame();

            break;



        case WM_ENTERMENULOOP:

            g_bHasFocus = !(BOOL)wParam;

            break;



        case WM_EXITMENULOOP:

            g_bHasFocus = TRUE;

            break;

 
WM_ENTERMENULOOP and WM_EXITMENULOOP are called whenever user clicks on a menu or leaves a menu. In case, where the Start menu was pressed, my window did not receive these messages, instead it was sent a WM_CANCELMODE message. WM_CANCELMODE is sent whenever a dialog box or a message box is displayed. But when I click on the Start menu again to close the popup, no message was sent to the main window. So I decided to display a small text on the screen which says "Tap here to continue.." and it works pretty well. But there is one problem however, which I’ll come to in a moment. I use DisplayTextOnScreen() to draw text directly on the primary surface,



void DisplayTextOnScreen(TCHAR *tszStr)

{

    HDC hdc;

    RECT rc = {0,}, destRect = {0,}, srcRect = {0,};

    int nMsg;

    SIZE size = {0,};

    HRESULT hr;



    if (g_primarySurface->GetDC(&hdc) == DD_OK)

    {

        SetBkColor(hdc, RGB(115, 115, 115));

        SetTextColor(hdc, RGB(255, 255, 255));

        GetClientRect(g_hWnd, &rc);



        nMsg = lstrlen(tszStr);

        GetTextExtentPoint(hdc, tszStr, nMsg, &size);



        ExtTextOut(hdc,

            (rc.right – size.cx)/2 – 25,

            rc.bottom – size.cy*2,

            //(rc.bottom – size.cy)/2,// – (rc.bottom – size.cy)/4,

            ETO_OPAQUE,

            NULL,

            tszStr,

            nMsg,

            NULL);





        g_primarySurface->ReleaseDC(hdc);

    }

}

And of course, you also have to handle WM_LBUTTONDOWN or WM_LBUTTONUP events to find out if the user clicked on the client area and then change the state variable so the marble can continue to move again.

 

There you go.

 

All the while writing this post I was taking regular backups and the thing never crashed. One of the reasons that makes me love those Murphy’s laws (:

 

And as always, here are a few screenshots,





         

          

You can tell which screen is the problem, right? Do you need a clue? Well, the problem is that when I click on Start and then on Menu Screen 4 happens. And vice-versa too. I am yet to figure out how to solve this, I do however have a slight idea of why it happens. Feel free to help me out.

Update: The problem mentioned above (Screen 4) is solved. Look here.

Written by Prabhu Kumar

September 12th, 2009 at 11:18 pm

Posted in Uncategorized

Applications: Displaying a notification using SHNOTIFICATIONDATA

with one comment

Sometimes you may want to display a notification to the user to inform her of an event or a pending task. You might say a message box will serve your purpose mostly, but there will be times when a message box is not the appropriate choice. For example when I was developing a call block application with a colleague of mine, we had to display the blocked call notification to the user, and the bubble notification was the most appropriate one. The notification shows an icon on the system tray and displays a message to the user. Lets see how we can do that.

To display a notification we use the SHNotificationAdd() function. This API takes a pointer to SHNOTIFICATIONDATA structure and this contains all the details about the notification that you want to show. The structure is defined as below,

typedef struct _SHNOTIFICATIONDATA{

  DWORD cbStruct;

  DWORD dwID;

  SHNP npPriority;

  DWORD csDuration;

  HICON hicon;

  DWORD grfFlags;

  CLSID clsid;

  HWND hwndSink;

  LPCTSTR pszHTML;

  LPCTSTR pszTitle;

  LPARAM lParam;

  union

  {

    SOFTKEYMENU skm;

    SOFTKEYNOTIFY rgskn[NOTIF_NUM_SOFTKEYS];

  }

  LPCTSTR pszTodaySK;

  LPCTSTR pszTodayExec;

} SHNOTIFICATIONDATA;


cbStruct: size of the structure in bytes

dwID: identifier of this notification, you can give any unique number here

npPriority: priority of the notification. This can take two values, SHNP_INFORM or SHNP_ICONIC

csDuration: duration in seconds, contains the number of seconds that the notification should be displayed for

hicon: handle to the icon which will be displayed on the tray

grfFlags: this contains some flags for the notification, this can take values like, SHNF_CRITICAL (displays the notification with a red border), SHNF_DISPLAYON (the device display is forced to turn on), SHNF_HASMENU (the notification is created with a softkey bar), SHNF_SILENT (does not vibrate or play a sound on the device) etc

clsid: defines a CLSID (a GUID) for the notification, you can create a GUID by using the GuidGen.exe tool which ships with Visual Studio

hwndSink: handle to the window which will receive messages from the notification (for e.g. if the user selects a menu item)

pszHTML: HTML content of the notification

pszTitle: contains the title of the notification

lParam: user defined param

skm: SOFTKEYMENU structure that defines menu for the softkey bar, the SHNF_HASMENU flag in grfFlags member must be set

rgskn: used if the notification is to have two softkeys

pszTodaySK: this contains the string that is displayed on the left softkey when csDuration seconds have elapsed, by default the text used is "Notification"

pszTodayExec: defines the name of the executable file which will run when the user presses the left softkey

The below program displays a notification for 5 seconds,



// {1F1C029E-95B2-4b5d-A2C5-AEF74BFCA979}

static const GUID CLSID_SHOW_NOTI =

{ 0x1f1c029e, 0x95b2, 0x4b5d, { 0xa2, 0xc5, 0xae, 0xf7, 0x4b, 0xfc, 0xa9, 0x79 } };



static HINSTANCE g_hInst = NULL;



int WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR lpCmdLine, int nShowCmd)

{

   SHNOTIFICATIONDATA shNotiData = {0};



   g_hInst = hInst;



   shNotiData.cbStruct = sizeof(shNotiData);

   shNotiData.dwID = 1;

   shNotiData.npPriority = SHNP_INFORM;

   shNotiData.csDuration = 5; //time in seconds

   shNotiData.hicon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_ICON1));

   shNotiData.clsid = CLSID_SHOW_NOTI;

   //shNotiData.clsid = CLSID_SHNAPI_OemNotif1;

   shNotiData.grfFlags = SHNF_TITLETIME | SHNF_CRITICAL;//0;

   shNotiData.pszTitle = TEXT("My Notification");

   shNotiData.pszHTML = TEXT("<html><body>This program shows how to display a notification.</body></html>");

   shNotiData.rgskn[0].pszTitle = TEXT("Dismiss");

   shNotiData.rgskn[0].skc.wpCmd = 1001;

   shNotiData.pszTodaySK = TEXT("Alert!");



   Sleep(500);



   SHNotificationAdd(&shNotiData);



   return 0;



}

To remove a notification use SHNotificationRemove() api. And SHNotificationUpdate() and SHNotificationGetData(), update and get the information about a notification, respectively.

Below is a video of the above program, running.


Written by Prabhu Kumar

September 12th, 2009 at 11:17 pm

Posted in Uncategorized

Applications: Running an application when a specified event occurs

without comments

Recently, I saw a video by Jim Wilson on running applications at specific events. He used CeRunAppAtEvent() native api, from managed code, to register an application to run when the device wakes up. Lets look at the API,



BOOL CeRunAppAtEvent(TCHAR *pwszAppName, LONG lWhichEvent);

pwszAppName: string which specifies the name of the application to be started. This string can also specify a named event. In case of a named event, the event will be opened and signaled. The named event should be of the format:



"\\\\.\\Notofications\\NamedEvents\\Event Name"



"Event Name" is the application defined event name



lWhichEvent: Specifies the event at which the application has to be started, it can take the following values,

NOTIFICATION_EVENT_DEVICE_CHANGE

 A PC Card device changed.

NOTIFICATION_EVENT_INTERNET_PROXY_CHANGE

 The Internet Proxy used by the device has changed.

NOTIFICATION_EVENT_IR_DISCOVERED

 The device discovered a server by using infrared communications.

NOTIFICATION_EVENT_NET_CONNECT

 The device connected to a network.

NOTIFICATION_EVENT_NET_DISCONNECT

 The device disconnected from a network.

NOTIFICATION_EVENT_NONE

 No events occurred. Remove all event registrations for this application.

NOTIFICATION_EVENT_OFF_AC_POWER

 The user turned the alternating current (AC) power off.

NOTIFICATION_EVENT_ON_AC_POWER

 The user turned the AC power on.

NOTIFICATION_EVENT_RESTORE_END

 A full device data restore completed.

NOTIFICATION_EVENT_RNDIS_FN_DETECTED

 RNDISFN interface is instantiated.

NOTIFICATION_EVENT_RS232_DETECTED

 An RS232 connection was made.

NOTIFICATION_EVENT_SYNC_END

 Data synchronization finished.

NOTIFICATION_EVENT_TIME_CHANGE

 The system time changed.

NOTIFICATION_EVENT_TZ_CHANGE

 The time zone changed.

NOTIFICATION_EVENT_WAKEUP

 The device woke up.

 

NOTIFICATION_EVENT_NONE unregisters all the events associated with that application.

 

I have the following two functions,



void RegisterApp()

{

    BOOL ret;

    ret = CeRunAppAtEvent(TEXT("\\Windows\\BubbleBreaker.exe"), NOTIFICATION_EVENT_WAKEUP);

    if(!ret)

    {

        printf("RegisterApp:CeRunAppAtEvent failed, err:0x%x\r\n", GetLastError());

    }

}





void UnRegisterApp()

{

    BOOL ret;

    ret = CeRunAppAtEvent(TEXT("\\Windows\\BubbleBreaker.exe"), NOTIFICATION_EVENT_NONE);

    if(!ret)

    {

        printf("UnRegisterApp:CeRunAppAtEvent failed, err:0x%x\r\n", GetLastError());

    }

}

So, whenever the device wakes up from sleep, the BubbleBreaker game will start. To simulate a device sleep and wake up on the emulator, select File -> Save State and Exit from the Emulator menu. To wake the device up, select Tools -> Connect to Device… from Visual Studio (2005).

Here’s a video for you folks,

Written by Prabhu Kumar

September 12th, 2009 at 11:17 pm

Posted in Uncategorized

Submitting applications to Windows Mobile Marketplace

without comments

Here is a nice walk through video by WMExperts on submitting applications to the windows marketplace, hit the link below.



Submitting apps to marketplace.

Written by Prabhu Kumar

September 12th, 2009 at 11:16 pm

Posted in Uncategorized