Applications: Problems with static text control

Today at work, I was working on a small application with my colleague. We had dialog box with a button, an edit control and a static control. What we wanted to do was show the status of the program on the static control once the button was pressed. And the code looked something like this:

case WM_COMMAND:

{

    wmID = LOWORD(wParam);

    wmEvent = HIWORD(wParam);

    switch(wmID)

    {

        case IDM_BUTTON_GMC:

        {

            SetWindowText(GetDlgItem(hDlg, IDC_STATIC_STATUS), L"In progress..");



            /*

            Do some work

            */



            SetWindowText(GetDlgItem(hDlg, IDC_STATIC_STATUS), L"Done.");

        }

        break;

    }

    .

    .

    .

}

break;

The default text on the static control was blank. When I ran the program we noticed that the static control never displayed "In Progress..". After finishing the actual meat of the job, the static control displayed "Done". We wondered whether it was because the actual work was happening too fast, and we put a sleep of 1 second after the first SetWindowText() call, but it didn’t solve the problem. We sat wondering about why this was happening. We checked the return value of the first SetWindowText call and it was non-zero which meant the call was successful. Another colleague of ours walked by and we explained the problem to her briefly, and she stared for a moment and said, UpdateWindow. And we all went "Aww…". So we put UpdateWindow(GetDlgItem(hDlg, IDC_STATIC_STATUS)); after both the calls and it worked, as expected. Painting is one of the most costliest operation and runs at a very low priority. So no wonder the calls before weren’t updating the text immediately. Sometimes we are all so occupied with bigger things that we miss out on the small ones.

Applications: Problems with painting DialogBox

Today I was working on an application with a colleague. The application contained many dialogs, well, it was a settings related app which let the user perform and view all sort of settings on the phone. The application was medium sized with the original code very badly written. And we were supposed to clean up the code and change the look and feel of the various Dialogs used in the app. And also make sure that the same code base worked on both Standard and Professional devices, when compiled with different flags of course. We were almost through most of the things but one problem kept bothering us for a long time. One of the dialogs in the app never displayed correctly. And it was strange because it was exactly the same as other dialogs. The DialogProc of the dialog looked something like this:

BOOL CALLBACK DlgProc((HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)

{

    BOOL fRet = FALSE;

    switch(uMessage)

    {

        case WM_INITDIALOG:

        {

            /*

            Do SHInitDialog()

            */

            /*

            Do SHCreateMenuBar()

            */

            /*

            Setup controls in the dialog

            */

            return TRUE;

        }

        break;



        case WM_COMMAND:

        {

            /*

            Handle all controls here

            */

            return TRUE;

        }

        break;



        case WM_ACTIVATE:

        {

            /*

            Do some stuff

            */

        }

        break;



        case WM_PAINT:

        {

            hdc = BeginPaint(hDlg, &ps);



            EndPaint(hDlg, &ps);

        }

        break;



        case WM_CLOSE:

        {

            EndDialog(hDlg, uMessage);

        }

        break;

    }



    fRet = TRUE;



    return fRet;

}

Well, the code was a lot more messy than is shown here. Anyways, we sat wondering why only this dialog was not getting displayed properly. The background of the dialog was not at all drawn and the controls on the dialog appeared shabbily. We thought that a certain sequence of events caused the dialog to be displayed that way, so we tried calling the dialog from different places but the result still the same. We checked and compared the dialog properties of various dialogs with this one. They were insignificantly different but we still tried to make them exactly same, but still no luck yet. After about 2 hours I noticed the return value of the dialog proc and saw that it was always returning true no matter what. And I knew. How in the silly world can I miss such an important thing for this long! The return value of a dialog proc is a really important thing. Returning TRUE from the function means that you have handled the message and the OS may or may not take further action, mostly it wont. Returning FALSE would tell the OS that you have not handled the message and the OS would now handle the message and execute the default handling for that message. Why our dialog was not getting displayed correctly was that, we were returning TRUE for all messages, including WM_PAINT. So the OS thought that I have handled the paint message it does not need to take any action. And that is why there were problems in painting the dialog. We changed the return value to be TRUE or FALSE depending on whether we handled that message or not and it worked.

 

Applications: Creating a simple UI application from scratch

In this series of posts I will show how we can create a basic UI application for a Windows Mobile Professional device from scratch, without having to go through all the hidden stuff that the wizard does to your application (the stdafx’s). And there is another reason why I would want to create an application from scratch, while porting applications, which were built using VS2005, to a BSP (in a build environment) I have faced lot of annoying problems. It throws a lot of compilation errors, and its a pain to resolve them. To make the application a little more useful, the application will display all the current running processes, and you will be able to refresh the view. I am planning to use a List View control for this, instead of a stale list box for two reasons, One I have never used a list view control and two you can display icons in a list view, cool right? Anyways, I have just started with the application. More posts coming soon.

Applications: Here is what I did over the weekend, Process Viewer

Well, I was just able to finish this small project of mine on time this weekend. But unfortunately I don’t have the time enough to write in detail about it. Anyways, I created a small Process Viewer. It shows all the current running processes in the system, shows the process id, number of threads running in the process and the load address of the process. The back-end didn’t take much time as compared to the UI itself. But I am happy that I finished it on time.

 

Here are a few screen shots of my Process Viewer:

and,

In the next few posts I will write about how I accomplished this task.

Plain text, ASCII, ANSI, UNICODE, UTF-8, UTF-16 confused?

I came across this wonderful post by Joel Spolsky  on characters, character sets, ANSI, ASCII, UNICODE and much much more. I have to admit that till now I thought plain text is ASCII is ANSI, all within 8-bits. And anything that takes up 2-bytes was unicode. Well, I couldn’t have been more wrong! Here are a few excerpts from his entry:

 

"All that stuff about "plain text = ascii = characters are 8 bits" is not only wrong, it’s hopelessly wrong, and if you’re still programming that way, you’re not much better than a medical doctor who doesn’t believe in germs."

 

"Some people are under the misconception that Unicode is simply a 16-bit code where each character takes 16 bits and therefore there are 65,536 possible characters. This is not, actually, correct. It is the single most common myth about Unicode, so if you thought that, don’t feel bad."

 

Don’t miss this one. Grab a read at:

 

Better learn late than never (:

Applications: Creating a simple UI application from scratch, Part 1

Lets create a simple UI application for Win Mob 6 professional device, from scratch. When you create the project using the wizard you can see that basic code is already there. The main function, the window procedure etc. And then there’s stdafx. I never quite understood what they are there for, but I guess they must be important. But when I create the application from scratch, there will no stdafx’s. Just a plain simple file with a UI that shows up.

To start, create a new project for Win32 smart devices, (I have mentioned about this is my previous posts). At the last screen of the wizard, check the “Empty Project” checkbox. It will be unchecked by default. When you click finish, you’ll see that the project is empty! (-:

Ok, so the first thing we do is add a cpp file to our sources directory.  Lets call it FromScratch.cpp.  Add the following code, just to test if things are alright:

#include <windows.h>

#include <aygshell.h>

 

int WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,

                  LPTSTR lpCmdLine, int nCmdShow)

{

      MessageBox(NULL, L"Hi there..", L"Info", MB_OK);

      return 0;

}

Hit compile and run. You should see a message box. windows.h is the standard header file and aygshell contains UI stuff.  WinMain is the entry point of your application and takes in 4 parameters. The first, hInst, is the handle to the instance of your module. The second is the handle to the previous instance of this program if it is already running. This param is always passed as NULL, so don’t bother. lpCmdLine is the command line arguments, if any and the last param, nCmdShow, determines how the window is to be shown. Most of the times I just ignore all the params except hInst.

Lets now add some UI to it. First create the resource file, which will hold all our application’s resources. Right click on “Resource Files” and add a new item, name it FromScratch.rc. resource.h will also be created in your project. Double click the resource file and add a new dialog to the resource by using “Add resource..”. Add a new dialog for pocket pc portrait. Change the dialog properties, I renamed the title to “From Scratch”, changed the caption of the default label and changed the ID of the dialog to IDD_PPC_FROMSCRATCH. My dialog now looks like this:

Lets add a menu bar to our application. The thing with menu bar is, you need to define the menubar in a separate text file (.rc2 file for e.g.) and then include this text file in the .rc file. This is a known bug. So add a new text file to the project, call it “FromScratch.rc2” and include it in the .rc file.

This menu bar will have two entries, the left soft key as “Exit” and the right soft key as “Refresh”. I ll let you in on refresh later.

We need two string resources for “Exit” and “Refresh” and two codes which will be sent when the user clicks on them. Define the menubar in the .rc2 file:

IDR_MENU_FROMSCRATCH SHMENUBAR DISCARDABLE

BEGIN

    IDR_MENU_FROMSCRATCH,

    2,

 

    I_IMAGENONE, IDM_EXIT, TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE,

    IDS_EXIT, 0, NOMENU,

   

    I_IMAGENONE, IDM_REFRESH, TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE,

    IDS_REFRESH, 0, NOMENU,

END

 

Add the following entries in the resource.h file:

#define IDR_MENU_FROMSCRATCH              501

#define IDS_EXIT                          502

#define IDS_REFRESH                       503

#define IDM_EXIT                          504

#define IDM_REFRESH                       505

 

And add the following stringtable in the .rc file:

 

STRINGTABLE

BEING

      IDS_EXIT                "Exit"

      IDS_REFRESH             "Refresh"

END

 

I have explained all these in my previous posts, so I am not going to repeat them.

Include resource.h in FromScratch.cpp. Add aygshell.lib in Additional dependencies (go to project properties, linker -> input). As mentioned before we need aygshell.lib for SHInitDialog, SHCreateMenuBar etc.

Now all we need to do is, add the code for FromScractchDlgProc and invoke the dialog from main.

Here is the code for FromScratchDlgProc:

BOOL CALLBACK FromScratchDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)

{

      int wmID, wmEvent;

      PAINTSTRUCT ps;

      HDC hdc;

 

      switch(uMessage)

      {

            case WM_INITDIALOG:

                  {

                        SHINITDLGINFO shidi;

                        SHMENUBARINFO mbi;

 

                        memset(&shidi, 0, sizeof(shidi));

                        memset(&mbi, 0, sizeof(mbi));

 

                        shidi.dwMask = SHIDIM_FLAGS;

                        shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU;

                        shidi.hDlg = hDlg;

                        SHInitDialog(&shidi);

 

                        mbi.cbSize = sizeof(mbi);

                        mbi.hwndParent = hDlg;

                        mbi.nToolBarId = IDR_MENU_FROMSCRATCH;

                        mbi.hInstRes = g_hInst;

 

                        if(!SHCreateMenuBar(&mbi))

                        {

                              printf("FromScratch: Error creating menu bar, errcode:0x%x\n", GetLastError());

                        }

                  }

                  return TRUE;

 

            case WM_COMMAND:

                  {

                        wmID = LOWORD(wParam);

                        wmEvent = HIWORD(wParam);

 

                        switch(wmID)

                        {

                              case IDM_EXIT:

                                    EndDialog(hDlg, uMessage);

                                    break;

 

                              case IDM_REFRESH:

                                    //for now

                                    EndDialog(hDlg, uMessage);

                                    break;

                        }

                  }

                  break;

                 

            case WM_PAINT:

                  {

                        hdc = BeginPaint(hDlg, &ps);

 

                        EndPaint(hDlg, &ps);

                  }

                  break;

 

      }

 

      return FALSE;

}

Again, I have explained about this in my previous posts. Basically, WM_INITDIALOG is where you do your initialization part, set the dialog as fullscreen, add the menubar etc. There are no controls yet, so the WM_COMMAND is empty. However, we need to add the handling for the two menus that we added before. Invoke this dialog from main function:

DialogBox(hInst, MAKEINTRESOURCE(IDD_PPC_FROMSCRATCH), NULL, FromScratchDlgProc);

Create a global variable to store the instance handle of the program:

HINSTANCE g_hInst;

And in WinMain, store the hInst in g_hInst. WinMain now looks like:

int WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,

                  LPTSTR lpCmdLine, int nCmdShow)

{

      //MessageBox(NULL, L"Hi there..", L"Info", MB_OK);

 

      g_hInst = hInst;

 

      DialogBox(hInst, MAKEINTRESOURCE(IDD_PPC_FROMSCRATCH), NULL, FromScratchDlgProc);

      return 0;

}

And you’re done. Here is the screenshot:

Applications: Creating a simple UI application from scratch, Process Viewer, Part 2Applications: Creating a simple UI application from scratch, Process Viewer, Part 2

In this series of posts lets go about creating a simple process viewer, as promised earlier. This post is basically based around a post by Bruce Eitman. We use Toolhelp32 api’s to get information about the running processes and other info too, like the process id, number of threads running in the process’s context etc. I recommend reading Bruce’s post before proceeding here. That will save me time from explaining the backend stuff and I can concentrate on the UI.



Ok, so lets go about creating the UI. Create a program similar to the one I had created in my previous post. This dialog contains a List view control and a group box, shown below:


Set the "View" property of the list view control as Report, "No Column Header" to false. I have set the ID to IDC_LISTVIEW_PROCESSES. Create 3 static text controls to hold the Process id, No of threads and the load address. Add these three static controls into a group box, name the group box "Process Info". Remember to create the three static text controls before creating the group box. I was trying the other way around, by creating the group box first, the text controls would not show up, they were hidden behind the group box. Invoke this dialog from WinMain and check that the dialog is showing up correctly. Here is the WinMain function:


int WinMain(HINSTANCE hInst,

            HINSTANCE hPrevInst,

            LPTSTR lpCmdLine,

            int nCmdShow)

{



    INITCOMMONCONTROLSEX icex;



    g_hInst = hInst;



    memset(&icex, 0, sizeof(icex));

    icex.dwSize = sizeof(INITCOMMONCONTROLSEX);

    icex.dwICC = ICC_LISTVIEW_CLASSES;



    InitCommonControlsEx(&icex);



    //MessageBox(NULL, L"Hi", L"Info", MB_OK);

    DialogBox(hInst, MAKEINTRESOURCE(IDD_PPC_PROCVIEWER), NULL, ProcessViewerDlgProc);



    return 0;

}

InitCommonControlsEx() is used to register specific common controls classes from the common control dynamic-link library. In this case a List View control. And then there’s the call to DialogBox() which shows up our UI.

About ProcessViewerDlgProc, for now, you could use the same skeleton dialog proc from my previous post.



Well, that is all for now. In the next post, we see how to initialise the List View control headers, get the information about the process and populate the list view control with the process names.

Applications: Creating a simple UI application from scratch, Process Viewer, Part 3

Let me paste the code for ProcessViewerDlgProc function, and then we will see how each part works. Here is the code:

BOOL CALLBACK ProcessViewerDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)

{

    int wmID, wmEvent, procCount;

    PAINTSTRUCT ps;

    HDC hdc;



    switch(uMessage)

    {

        case WM_INITDIALOG:

            {

                SHINITDLGINFO shidi;

                SHMENUBARINFO mbi;



                memset(&shidi, 0, sizeof(shidi));

                memset(&mbi, 0, sizeof(mbi));



                shidi.dwMask = SHIDIM_FLAGS;

                shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU;

                shidi.hDlg = hDlg;

                SHInitDialog(&shidi);



                mbi.cbSize = sizeof(mbi);

                mbi.hwndParent = hDlg;

                mbi.nToolBarId = IDR_MENU_PROCVIEWER;

                mbi.hInstRes = g_hInst;



                if(!SHCreateMenuBar(&mbi))

                {

                    printf("ProcViewer: Error creating menu bar, errcode:0x%x\n", GetLastError());

                }

                else

                {

                    g_hWndMenuBar = mbi.hwndMB;

                }



                AllignComponents(hDlg);



                //initialise the list view control column headers

                InitializeListViewControl(hDlg);



                procCount = GetRunningProcesses();



                RefreshProcessListView(hDlg, procCount);

            }

            return TRUE;



        case WM_COMMAND:

            {

                wmID = LOWORD(wParam);

                wmEvent = HIWORD(wParam);



                switch(wmID)

                {

                    case IDM_EXIT:

                        EndDialog(hDlg, IDM_EXIT);

                        break;



                    case IDM_REFRESH:

                        UpdateProcessView(hDlg);

                        break;

                }



            }

            break;



        case WM_PAINT:

            {

                hdc = BeginPaint(hDlg, &ps);



                //add drawing code here



                EndPaint(hDlg, &ps);

            }

            break;



        case WM_NOTIFY:

            {

                switch(((LPNMHDR)lParam)->code)

                {

                    case LVN_ITEMCHANGED:

                        {

                            LPNMLISTVIEW lpnm = (LPNMLISTVIEW)lParam;

                           

                            //get info about which item was selected.

                            int temp = lpnm->uNewState ^ lpnm->uOldState;



                            /*

                            when you select an item you get about 3 LVN_ITEMCHANGED notification.

                            So process only one of them, i.e when the item is selected and focused, both.

                            */

                            if((temp & LVIS_SELECTED) && (temp & LVIS_FOCUSED))

                            {

                                if(lpnm->iItem < MAX_PROCESSES)

                                {

                                    ShowProcInfo(hDlg, &ProcessList[lpnm->iItem]);

                                }

                            }



                        }

                        break;

                }

            }

            break;



        case WM_CLOSE:

            {

                EndDialog(hDlg, uMessage);

                return TRUE;

            }



    }



    return FALSE;

}

I will concentrate only on WM_INITDIALOG and WM_NOTIFY and leave the other messages. They are pretty much self explanatory and you probably know what they are for already.

 

The WM_INITDIALOG message is sent when our dialog is to be initialized just before it is shown to the user. This is where we set up the UI controls in the dialog, move them around, align them and of course associate a menu bar with our dialog.  The SHInitDialog and SHCreateMenuBar functions are standard and are used to display the dialog in full screen and add a menubar, respectively. The menu bar here has "Exit" as its left softkey and "Refresh" the right softkey. The next function I call is AllignComponents(). This is basically to beautify the dialog and move around the controls so they look nice. Before going to the AllignComponents function, here are a few globals that I use:

HINSTANCE g_hInst;

HWND g_hWndMenuBar;

PROCESSENTRY32 ProcessList[MAX_PROCESSES];

g_hInst, holds the instance handle of our application. g_hWndMenuBar stores the handle to the menubar created in WM_INITDIALOG. And finally, ProcessList is an array of PROCESSENTRY32 structures, which will hold the list of current running processes. MAX_PROCESSES is defined as:

#define MAX_PROCESSES    32

Now back to the AllignComponents() function, here is the code:



//this function alligns the controls on the dialog, beautifies.

BOOL AllignComponents(HWND hWnd)

{

    HWND hWndListView = NULL;

    HWND hTemp;

    RECT rectMainDlg, rect;

    int x, y, width, height;

   

    memset(&rectMainDlg, 0 , sizeof(RECT));



    //get the client area of the main dialog

    GetClientRect(hWnd, &rectMainDlg);



    hWndListView = GetDlgItem(hWnd, IDC_LISTVIEW_PROCESSES);



    if(!hWndListView)

    {

        printf("GetDlgItem failed errcode:%d  line:%d\n", GetLastError(), __LINE__);

        return FALSE;

    }



    //position List view wrt to main dialog

    x = rectMainDlg.left + 1;

    y = rectMainDlg.top + 1;

    width = rectMainDlg.right – rectMainDlg.left – 2;

    height = (rectMainDlg.bottom – rectMainDlg.top)/2;



    if(!MoveWindow(hWndListView, x, y, width, height, FALSE))

    {

        printf("MoveWindow failed! errcode:%d  line:%d\n", GetLastError(), __LINE__);

    }





    //position GROUPBOX wrt List view

    hTemp = GetDlgItem(hWnd, IDC_STATIC_GROUPBOX);



    if(!hTemp)

    {

        printf("GetDlgItem failed errcode:%d  line:%d\n", GetLastError(), __LINE__);

        return FALSE;

    }



    memset(&rect, 0, sizeof(RECT));

    GetWindowRect(hWndListView, &rect);



    x = rect.left;

    y = rectMainDlg.top + (rect.bottom – rect.top) + 5;

    width = rect.right – rect.left;

    height = (rectMainDlg.bottom – rectMainDlg.top)/2 – 15;



    if(!MoveWindow(hTemp, x, y, width, height, FALSE))

    {

        printf("MoveWindow failed errcode:%d  line:%d\n", GetLastError(), __LINE__);

    }



    //beautify other labels



    memset(&rect, 0, sizeof(RECT));

    GetWindowRect(GetDlgItem(hWnd, IDC_STATIC_GROUPBOX), &rect);



    x = rect.left + 8;

    y = rect.top + 4;

    width = rect.right – rect.left – 20;

    height = 20;



    //beautify Proc ID label wrt to group box

    if(!MoveWindow(GetDlgItem(hWnd, IDC_STATIC_PROCID), x, y, width, height, FALSE))

    {

        printf("MoveWindow failed errcode:%d  line:%d\n", GetLastError(), __LINE__);

    }



    //beautify No of Threads label wrt to PROC ID label

    y = y + height + 4;

    if(!MoveWindow(GetDlgItem(hWnd, IDC_STATIC_NTHREADS), x, y, width, height, FALSE))

    {

        printf("MoveWindow failed errcode:%d  line:%d\n", GetLastError(), __LINE__);

    }



    //beautify Load Addr label wrt No Of Threads label

    y = y + height + 4;

    if(!MoveWindow(GetDlgItem(hWnd, IDC_STATIC_LOADADDR), x, y, width, height, FALSE))

    {

        printf("MoveWindow failed errcode:%d  line:%d\n", GetLastError(), __LINE__);

    }



    return TRUE;

}

This function receives the handle to the dialog and uses the MoveWindow() api to position and size the controls with respect to the dialog and other controls. The x, y co-ordinate values that I have used take into account the screen size (by getting the client area of a full screen dialog) so the UI should not appear terribly bad when the program is run on a device with different screen size and resolution, I haven’t tested it on any device though. Nothing else I see to be explained about this function, so lets move on.

 

The next function I call is InitializeListViewControl(), here is the code:



BOOL InitializeListViewControl(HWND hDlg)

{

    HWND hWndListView = NULL;

    LVCOLUMN lvc;

    RECT rect;



    //column headers

    TCHAR *szColText[2] = { TEXT("S. No"), TEXT("Process Name") };

    const int numCols = 2;



    memset(&lvc, 0, sizeof(lvc));



    hWndListView = GetDlgItem(hDlg, IDC_LISTVIEW_PROCESSES);



    if(!hWndListView)

    {

        printf("+++ Failed to get list view handle! errcode:%d", GetLastError());

        return FALSE;

    }



    //get the window rect of list view, we’ll use this to set column widths

    GetWindowRect(hWndListView, &rect);



    //set list view styles

    SendMessage(hWndListView, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_REPORT);

    //SendMessage(hWndListView, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_GRIDLINES);

    SendMessage(hWndListView, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_SINGLESEL);

    SendMessage(hWndListView, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);



    lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;



    //add columns to list view

    for(int i=0; i < numCols; i++)

    {

        lvc.iSubItem = i;

        lvc.pszText = szColText[i];



        //set 20% of width for S.No and 80% width for Process Name

        if(i == 0)

        {

            lvc.cx = (rect.right – rect.left)/5;

        }

        else if(i == 1)

        {

            lvc.cx = ((rect.right – rect.left)*4)/5;

        }



        lvc.fmt = LVCFMT_LEFT;



        if (ListView_InsertColumn(hWndListView, i, &lvc) == -1)

        {

            printf("+++ ListView_InsertColumn failed! errcode:%d\n", GetLastError());

            return FALSE;

        }   

    }



    //InsertItems(hWndListView);

    //ListView_SetItemState(hWndListView, 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);



    return TRUE;

}

This function sets up our List view control. szColText contains the text of the column headers, you can see that I am using a list control with two columns, first displaying "S. No" and the second column "Process Name". To add a column to the list view control we use the LVCOLUMN structure and ListView_InsertColumn() macro. This macro basically is wrap around the LVM_INSERTCOLUMN message. Get the handle to the list view control and send it a few extended style messages. Setting its view style to Report, making it single select rows and select the full row. The "mask" member of the LVCOLUMN struct specifies, which other members in the struct are valid, in this case, format, column width, text and subitem members. Then in the for loop I set up the iSubItem, pszText, cx (column width) and fmt members. iSubItem is the zero based index of the column, pszText contains the text to be displayed and the text is left alligned in the column. Since there are only two columns I use, I set the width of the first one to 1/5th of the list view controls width and the second to 4/5ths. And finally I use the ListView_InsertColumn macro and pass in the LVCOLUMN structure.

 

Next is the call to GetRunningProcesses(). This function fills up the global, ProcessList, array of PROCESSENTRY32 structure.

//this function fills the ProcessList array with all the current running processes

DWORD GetRunningProcesses()

{

    int index = 0;

    HANDLE hSnapShot;

    PROCESSENTRY32 *pProcess = &ProcessList[0];

    PROCESSENTRY32 prevProcess;

   

    hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPNOHEAPS, 0);



    if(hSnapShot == INVALID_HANDLE_VALUE)

    {

        printf("CreateToolhelp32Snapshot failed errcode:%d  line:%d\n", GetLastError(), __LINE__);

        return FALSE;

    }



    memset(pProcess, 0, sizeof(PROCESSENTRY32));

    pProcess->dwSize = sizeof(PROCESSENTRY32);



    if(!Process32First(hSnapShot, pProcess))

    {

        printf("Process32First failed errcode:%d  line:%d\n", GetLastError(), __LINE__);

        return -1;

    }



    memcpy(&prevProcess, pProcess, sizeof(PROCESSENTRY32));

    index = 1;



    while(Process32Next(hSnapShot, &prevProcess))

    {

        if(++index >= MAX_PROCESSES)

            break;



        pProcess++;

        memcpy(pProcess, &prevProcess, sizeof(PROCESSENTRY32));



    }



    CloseToolhelp32Snapshot(hSnapShot);



    return index;

}

If you have read Bruce’s post, then there is nothing much for me to explain here. I use a different algo/logic from what Bruce has used. Well, I was using the same code as Bruce’s first, but I found out a minor flaw with his code that would put the same entry twice in the array at the end. I have to leave a comment on his post ;). Anyways, another thing that is worth mentioning is this, according to the documentation for Process32Next:

BOOL Process32Next(

                                  HANDLE hSnapshot,

                                  LPPROCESSENTRY32 *lppe);



hSnapshot: handle to the snapshot returned from a previous call to CreateToolhelp32Snapshot function.

lppe: Pointer to a PROCESSENTRY32 structure.

What this piece of information doesn’t tell you is the second parameter must point a PROCESSENTRY32 structure returned from a previous call to Process32First or Process32Next. Here’s some work for the Windows Mobile documentation team (;. This function returns the total number of running processes and the ProcessList array is filled with all information about the running processes waiting to be used.

 

 

And the last function I call in WM_INITDIALOG is RefreshProcessListView(). This function uses the data contained in ProcessList array and fills the list view control with this data. The code follows:



BOOL RefreshProcessListView(HWND hDlg, int procCount)

{

    LVITEM lvi;

    TCHAR szSNo[8] = TEXT("");

    HWND hListView = GetDlgItem(hDlg, IDC_LISTVIEW_PROCESSES);



    if(!hListView)

    {

        printf("GetDlgItem failed errcode:%d  line:%d\n", GetLastError(), __LINE__);

        return FALSE;

    }



    memset(&lvi, 0, sizeof(LVITEM));



    //delete all items from list view

    SendMessage(hListView, LVM_DELETEALLITEMS, 0, 0);



    lvi.mask = LVIF_TEXT | LVIF_STATE;

    lvi.state = 0;

    lvi.stateMask = 0;



    for(int i = 0; i < procCount; i++)

    {

        wsprintf(szSNo, L"%d", i+1);

        lvi.iItem = i;

        lvi.iSubItem = 0;

        lvi.pszText = szSNo;

        if(ListView_InsertItem(hListView, &lvi) == -1)

        {

            printf("ListView_InsertItem failed errcode:%d  line:%d\n", GetLastError(), __LINE__);

        }



        lvi.iItem = i;

        lvi.iSubItem = 1;

        lvi.pszText = ProcessList[i].szExeFile;

        if(ListView_SetItem(hListView, &lvi) == -1)

        {

            printf("ListView_SetItem failed errcode:%d  line:%d\n", GetLastError(), __LINE__);

        }

    }



    //select the first item in the list (default)

    ListView_SetItemState(hListView, 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);

    ShowProcInfo(hDlg, &ProcessList[0]);



    return TRUE;

}

To add a row in the list view control we use the LVITEM structure and ListView_InsertItem macro. This is a little interesting and I took some time to figure out ListView_InsertItem and ListView_SetItem. You can see that both these macros are being used in this function. Here is the crux, ListView_InsertItem is used when you are inserting the first item in the row. And once the first element in the row is added, we use ListView_SetItem to add more items in the same row. And that is why, ListView_InsertItem always expects iSubItem to be zero. Other things here are similar to inserting a column, you have mask specifying which fields are valid, iItem is the row number, iSubitem is the column number. ListView_InsertItem inserts an item at i’th row and 0th column and ListView_SetItem adds an item to the i’th row and 1st column (column numbers are zero based). Use the szExeFile member of the PROCESSENTRY32 structure to get the name of the executable file for that program.

ListView_SetItemState(hListView, 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);

Selects the first item in the list view control by default, when the dialog shows up.

 

And finally the ShowProcInfo function:

//update the text control under group box

BOOL ShowProcInfo(HWND hDlg, PROCESSENTRY32 *pProcess)

{

    TCHAR wszStr[64] = TEXT("");

    HWND hTemp;

   

    hTemp = GetDlgItem(hDlg, IDC_STATIC_PROCID);

    if(hTemp)

    {

        //wsprintf(wszStr, L"Process Id: %x", pProcess->th32ProcessID);

        wsprintf(wszStr, L"%-18s 0x%08x", L"Process Id:", pProcess->th32ProcessID);

        SetWindowText(hTemp, wszStr);

    }



    hTemp = GetDlgItem(hDlg, IDC_STATIC_NTHREADS);

    if(hTemp)

    {

        //wsprintf(wszStr, L"No of Threads: %d", pProcess->cntThreads);

        wsprintf(wszStr, L"%-15s %d", L"No of Threads:", pProcess->cntThreads);

        SetWindowText(hTemp, wszStr);

    }



    hTemp = GetDlgItem(hDlg, IDC_STATIC_LOADADDR);

    if(hTemp)

    {

        //wsprintf(wszStr, L"Load Address: %x", pProcess->th32MemoryBase);

        wsprintf(wszStr, L"%-15s 0x%08x", L"Load Address:", pProcess->th32MemoryBase);

        SetWindowText(hTemp, wszStr);

    }



    return TRUE;

}

This function updates the static text controls to show the info related to the currently selcted process in the list view control. We use the th32ProcessID, cntThreads and th32MemoryBase members of PROCESSENTRY32 structure to display the correct info. So by default the first entry in the list view control will be selected and Process Info will show the information about the first entry.

 

I think this post has become a little too long, there is a bit left. I will cover that too (;

 

When the user selects the Refresh softkey, I call UpdateProcessView() function,

BOOL UpdateProcessView(HWND hDlg)

{

    int procCount = 0;



    procCount = GetRunningProcesses();

    RefreshProcessListView(hDlg, procCount);



    return TRUE;

}

This simply calls GetRunningProcesses() and RefreshProcessListView() functions to take a snapshot again and refresh the process list view.

 

And lastly, the WM_NOTIFY message, here it is again:

        case WM_NOTIFY:

            {

                switch(((LPNMHDR)lParam)->code)

                {

                    case LVN_ITEMCHANGED:

                        {

                            LPNMLISTVIEW lpnm = (LPNMLISTVIEW)lParam;

                           

                            //get info about which item was selected.

                            int temp = lpnm->uNewState ^ lpnm->uOldState;



                            /*

                            when you select an item you get about 3 LVN_ITEMCHANGED notification.

                            So process only one of them, i.e when the item is selected and focused, both.

                            */

                            if((temp & LVIS_SELECTED) && (temp & LVIS_FOCUSED))

                            {

                                if(lpnm->iItem < MAX_PROCESSES)

                                {

                                    ShowProcInfo(hDlg, &ProcessList[lpnm->iItem]);

                                }

                            }



                        }

                        break;

                }

            }

            break;

WM_NOTIFY message is sent by any child control to the parent when some property of the control changes, for e.g. the user selected some entry in the list view control. The WM_NOTIFY message is accompanied by the NMHDR structure which contains information about why the notify message was sent. Here, I check whether the notification is LVN_ITEMCHANGED, this is sent whenever there is a change in the selected state of the items in the list view control. You might have read the comment in the code, when the user selects an item in the list view control, LVN_ITEMCHANGED notification will be sent quite a few times, once for the item that was unselected and once for the item that was selected and so on. So I process the message only when the item was selected and focused both. For this we XOR the old state and the new state of the item and then check the bits for selected and focused. We get the index of the item that was both selected and focused and call ShowProcInfo to update the static controls under the group box. So when the user selects entries in the list view control, the process info is updated automatically. Simple.

Applications: PViewCE, Process Viewer provided with SDK samples.

Well, I was browsing through the samples that come with Windows Mobile SDK and was surprised to find that they provide a process viewer! You can find the sample at the following path:



<WinMob6Std_InstallDir>\Samples\Common\CPP\Win32\PViewCE



I ran the sample on the emulator and it provided much more features than the process viewer I built in the previous posts. You can view information about the modules loaded by a particular process, information about all the threads running in the process etc. The api’s used are Module32First(), Module32Next(), Thread32First() and Thread32Next() to get information about modules and threads respectively. And they work exactly like Process32First() and Process32Next() that we used.



Its not hard to add the features into our process viewer, except that I am a little lazy to do it.



Anyways, I came across another sample called SpinTest, which really excited me! So I am gonna dig deeper into the sample and find out how it works and lets see if we can do something fun from what we learn. You can find the sample in the following path:



<WinMob6Pro_InstallDir>\Samples\PocketPC\CPP\win32\Spintest