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

Tools I use

I think the tools that we use and the way we use them define us in some ways. Some people prefer to work with command prompts even though a GUI is available, some people always stick with their editors that they use and switching to a new editor is very unsettling. As you progress as a developer you become greatly attached to these tools and the first thing that we do on a new PC is setup the tools the way we want. So here goes the list of tools that I use.

TextPad
TextPad has always been the editor for me. Its a simple to use editor with powerful features. Be it its support for regular expressions, built in clip library or its smart cursor placement, all work flawlessly. I like its text search options and searching for texts in files isn’t very bad too. You can search for it in download dot com, a free to use version is available.

Everything (Search tool)
Well, I found this tool by chance and it is one of the best search tools that I have used. You can search for any file or folder on your PC by just typing a few characters of the file/folder name. The searches are lightning fast, literally, once it has indexed the files of course. A very light weight and easy to use tool and if you are a person who constantly has to look for files because you don’t remember where you put them, then this tool is for you. You still need to remember the file name though, well, at least a part of it. No tool is idiot proof you see.

You can find the tool here:
http://download.cnet.com/Everything/3000-2379_4-10890746.html?tag=mncol

Examine 32 (Searches for texts within files)

This is a tool that I use to search for texts within files. The interface isn’t very sleek but it gets the job done pretty well. I like the way it displays the search results and it allows me to open a particular file just by clicking on the search results.

You can find the tool here:

http://download.cnet.com/Examine32-Text-Search/3000-2248_4-10059590.html?tag=mncol


Source Insight
Now this is a great tool for source code browsing. You just build your project by adding all your source files into it and you’re done. You can open any file, jump to any symbol, go to definitions, go back to previous cursor positions, see the call graph and tons of more features.


Object Dock
I used this tool for a while to quickly access my most frequently used programs. I like to keep my desktop clean.  But sometimes it takes up too much cpu for too much long and that is not very pleasing. I still use it occasionally though.


If you want to try it, follow the link:

Update: Object Dock is out and SlickRun is in. Object Dock was hogging to much cpu.

Google
You could raise your eyebrows but its one of the best tools available out there. I always setup google as my homepage and the search results that I get work for me.




Well, thats about the tools I use. If you have any such tools which you find make you more productive, be sure to leave a note.


Update:8thApr09



Command prompt here powertoy
I forgot to mention another useful tool, Command Prompt here power toy.  Its a small utility program that adds a menu item to the explorer context menu which allows you to open the command prompt with the current directory set to the directory you clicked on. It saves a lot of time trying to navigate to a particular directory in command prompt, so you just right click, select ‘Open command prompt here..’ and off you go. The tool is free and can be downloaded from the link below:


http://download.cnet.com/Vista-XP-Command-Prompt-Here/3000-2248_4-10698774.html

Update:10thApr09

Beyond Compare
Beyond compare is a file and directory compare tool that I use at work. It is quite fast and the interface is neat. Compare options show up in the explorer context menus which is nice.

WinMerge is also used to do the same. One advantage WinMerge has over Beyond Compare is that its free. You can download it from the link below:

http://winmerge.org/



Applications: Creating a simple UI application for Windows Mobile 6 using Visual Studio 2005: Part 1

Time to do some actual work. As the title suggests, I will be creating a simple UI application for windows mobile 6 professional device using visual studio 2005 and improve it step by step. I will cover the developments over maybe 2 or 3 posts and lets see how it goes.



Make sure you have installed Visual Studio 2005 and Windows Mobile 6 professional SDK before you proceed.



Note: When creating new smart device projects with VS2005 you might get a script failure error saying that the project creation failed, its a known issue and happens when you have IE7 or IE8 installed. Strangely, I have IE6 SP2 on my machine but the same error still showed up. Follow the instructions here to resolve the problem:



http://blogs.msdn.com/vcblog/archive/2009/03/28/some-vs2005-and-vs2008-wizards-pop-up-script-error.aspx





Open VS2005, select File->new->Project. The project wizard shows up. In "Project Types" select "Smart Device" under "Visual C++". On the right side select "Win32 Smart Device Project" and choose a name and location for this project. Lets call it "HelloWorld".

The project wizard now shows up asking you to select the platform and other project settings. Select "Next". Your Windows Mobile 6 professional sdk should show up on the list of installed sdk’s. Add this sdk to the "Selected SDK’s list" and lets remove "Pocket PC 2003" which will be selected by default. Click Next. The final page lets you select the type of your project and other options. Select "Windows Application" and leave the "Additional Options" and "Add support for" sections as is. Do not select "Empty Project" or "ATL" or "MFC". We will see how to go about creating an empty project in a later post. Click "Finish" and you are done. Source files and resource files which were automatically generated by the project wizard will now show up in solution explorer.



I prefer to work with "Windows Mobile 6 professional emulator" rather than the classic emulator. Basically, the classic emulator does not support phone functionality, you can see that the ‘Talk’ and ‘End’ buttons are not present. I will cover using the phone functionality in the emulator in later posts. So for now, just select the professional emulator. (With default settings, there should be a drop down list box on the top-left which enables you to select the type of emulator.)



Open stdafx.cpp and compile it (ctrl+F7). Open HelloWorld.cpp and compile. Now build the solution (F7). This should build our project without any errors. HelloWorld.exe should now be present under the debug folder in our project path. In my case it is: F:\MyProjects\HelloWorld\HelloWorld\Windows Mobile 6 Professional SDK (ARMV4I)\Debug->HelloWorld.exe



From the "Debug" menu select "Start Debugging", the emulator will now start and your application should deploy on it. This is how the application should look:



Application running on emulator





So there it is. A dumb application really. Let us add some functionality to it in the next post.

Part 2
Part 3

Applications: Creating a simple UI application for Windows Mobile 6 using Visual Studio 2005: Part 2

Ok now that we have our basic application up and running, lets make some changes. First, I want to change the right soft key from "HELP" to "Menu", somehow "HELP" is not very appealing. One more thing, I use the resource editor only to add new resources to the project, any further changes or modification I like to do it by manually editing the resource files (To do that, just right click on the .rc file and select "View Code").



If you open HelloWorld.cpp and in function WndProc go to the case where WM_CREATE is handled, you will see that the menu bar is getting created there using the SHCreateMenuBar api. The nToolBarId member of SHMENUBARINFO contains IDR_MENU, which is the resource identifier of the menu bar. Now open HelloWorldppc.rc2 and you will see that the menu bar is defined as below:



IDR_MENU SHMENUBAR DISCARDABLE

BEGIN

    IDR_MENU_POPUP,

    2,



    I_IMAGENONE, IDM_OK, TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE,

    IDS_OK, 0, NOMENU,

   

    I_IMAGENONE, IDM_HELP, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE,

    IDS_HELP, 0, 0,

END



The first line says that IDR_MENU is a menubar. Everything in between BEGIN and END defines the menu bar. The first line under BEGIN identifies the popup menu which will be displayed when the user clicks on the MENU. If your MENUBAR does not have any popup menu’s, then you can specify a value of 0 here. In this case, the popup menu that will be displayed here, when the user clicks on "Menu" is IDR_MENU_POPUP, which we will define later. "2" specifies that the menu bar will have two entries, one left soft key and one right soft key. The first entry after "2" defines the left soft key and the next entry defines the right softkey. You can see that the left entry is for "OK" and the right entry is for "HELP". We will not change the first entry. Lets modify the second entry so that it defines our new entry, which is "Menu". So we change



    I_IMAGENONE, IDM_HELP, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_HELP, 0, 0,



to



       I_IMAGENONE, IDM_MENU, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_MENU, 0, 0,



This change is not absolutely necessary but I like to name my variables just right.



Now open resourceppc.h and make the same changes. Rename IDS_HELP to IDS_MENU and IDM_HELP to IDM_MENU. A few more changes and we are done. Open HelloWorld.rc and search for the string table that defines the value for IDS_HELP. Will be something like:



STRINGTABLE 

BEGIN

   .

   .

   IDS_HELP    "HELP"

END



Change this to:



STRINGTABLE 

BEGIN

   .

   .

   IDS_MENU    "Menu"

END





The items under the popup menu will be defined in HelloWorld.rc file. Although you can define it .rc2 file as well, but by default it will be present in the .rc file.



IDR_MENU_POPUP MENU DISCARDABLE

BEGIN

    POPUP "Help"

    BEGIN

        MENUITEM "About",        IDM_HELP_ABOUT

    END

END



This defines our original right soft key menu which is a popup. Change this entry so that "Menu" shows up instead of "HELP":



IDR_MENU_POPUP MENU DISCARDABLE

BEGIN

    POPUP "Menu"

    BEGIN

        MENUITEM "About",        IDM_HELP_ABOUT

    END

END



Thats it. The right soft key should now be renamed to "Menu". You might think that this is a lot of work just to rename a menu entry. But its not much. The changes in the .rc2 file were just for the sake of naming convention and you can leave that part if you wish but make sure that all your ID*_ macros are in sync.



Build the project and run. The application should now look like this:







Lets quickly add one more entry under Menu called "System Metric", which will show the screen width and height in a message box. Adding a menu entry is simple. Open HelloWorld.rc file where the popup items for the right menu are defined. They will be defined like this:



IDR_MENU_POPUP MENU DISCARDABLE

BEGIN

    POPUP "Menu"

    BEGIN

       MENUITEM "About",            IDM_HELP_ABOUT

    END

END



Just add another MENUITEM entry for "System Metric":



IDR_MENU_POPUP MENU DISCARDABLE

BEGIN

    POPUP "Menu"

    BEGIN

        MENUITEM "System Metric",    IDM_SYSTEM_METRIC

        MENUITEM "About",            IDM_HELP_ABOUT

    END

END



IDM_SYSTEM_METRIC is the command (or simply a number) that will be sent to our application when the user selects it. So you need to define this in the resourceppc.h header file so that the compiler can find it. You can add a menu separator by adding MENUITEM SEPARATOR as one of the items in the menu.



Add  "#define IDM_SYSTEM_METRIC 40003" to resourceppc.h. I chose 40003 because that number wasn’t yet used in my header file. The number doesn’t matter, just make sure that the number you use is unique and is not used already in this or some other header file. In large projects with many resources this could get messy, manually keeping track of numbers. When creating resources using the resource editor these entries are automatically created and unique.



Now we need to handle this menu item. That is, what happens when the user clicks on it. When the user selects the menu item, a WM_COMMAND will be sent to our application. This will end up in WndProc function. The LPARAM and WPARAM parameters will contain more information about which item in our application caused the WM_COMMAND message to be sent. In the WndProc function go the section which is handling WM_COMMAND. Under WM_COMMAND handling you will see that other messages like IDM_HELP_ABOUT and ID_OK are already being handled. This is where we need to add the entry for our IDM_SYSTEM_METRIC. Create the following entry just after the IDM_OK case:



case IDM_SYSTEM_METRIC:

{

    int width = -1, height = -1;

    WCHAR wOutStr[64] = L"";

    WCHAR wCaption[16] = L"";



    width =  GetSystemMetrics(SM_CXSCREEN);

    height = GetSystemMetrics(SM_CYSCREEN);



    if(!width || !height)

    {

        wsprintf(wOutStr, L"Failed to get system metrics.");

        wsprintf(wCaption, L"Error");

    }

    else

    {

        wsprintf(wOutStr, L"System width :%d\nSystem Height:%d", width, height);

        wsprintf(wCaption, L"Info");

    }



    MessageBox(hWnd, wOutStr, wCaption, MB_OK);



}

break;



In the message handling I use the GetSystemMetrics api to get the system width and system height values. If the api’s return error (zero) I contruct an error string otherwise I construct a string with the values I got and then show the string using a MessageBox. Simple.



This is how the UI looks:







and,








Part 1
Part 3