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

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

So now our program displays the system width and height in a Message box. Let us see how this message box evolves into a Dialog box.

 

Instead of displaying the system metrics in a Message box, we will display the same information in a full screen dialog box. The dialog box will have a menu bar, with the left soft key as "Back" and the right soft key as "Refresh". We’ll see what "Refresh" will do later.



First lets add the dialog box resource to our project. You can add a new dialog using the resource editor. Go to Resource view, right click on Dialog and "Add Resource..", from the list select POCKETPC_PORTRAIT dialog. This will add a new dialog resource to the project. Rename the dialog id to IDD_DIALOG_SYSMETRIC and change the title to "System Metric". Now add four static texts to the dialog. 2 containing the text "System Width:" and "System Height:" and the other two will contain the actual numbers.  Lets name the controls which will contain the width and height as IDC_STATIC_SYSWIDTH and IDS_STATIC_SYSHEIGHT. The dialog will now look like:

Lets create the menu bar for our new dialog. Add the following piece of code to HelloWorldppc.rc2:


IDR_MENU_SYSMETRIC SHMENUBAR DISCARDABLE

BEGIN

    IDR_MENU_SYSMETRIC,

    2,



    I_IMAGENONE, IDM_BACK, TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE,

    IDS_BACK, 0, NOMENU,

   

    I_IMAGENONE, IDM_REFRESH, TBSTATE_ENABLED, TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE,

    IDS_REFRESH, 0, NOMENU,

END

This defines the new menu bar, IDR_MENU_SYSMETRIC, for an explanation of the above see my previous post "Applications: Creating a simple UI application for Windows Mobile 6 using Visual Studio 2005: Part 2"



This menu bar does not have any popup menu. You can see that both the entries are TBSTYLE_BUTTON and NOMENU.



Now you need to #define the entries for IDR_MENU_SYSMETRIC, IDM_BACK, IDS_BACK, IDM_REFRESH, IDS_REFRESH in resourceppc.h. Make sure that the numbers you assign to these macros are unique.



The dialog and the menu bar are ready, now we just need to call DialogBox in our main code. So, in HelloWorld.cpp, replace the call to MessageBox under IDM_SYSTEM_METRIC with:



DialogBox(g_hInst, MAKEINTRESOURCE(IDD_DIALOG_SYSMETRIC), hWnd, SysMetricDlgProc);



The first parameter contains the instance handle of the module which contains the resource template of the dialog. The MAKEINTRESOURCE macro converts the number IDD_DIALOG_SYSMETRIC to a resource type, required by DialogBox. Third parameter specifies the parent window of the Dialog box. And the last parameter is the callback function which will handle the messages of our dialog.



Also, forward declare the callback function as follows:



BOOL CALLBACK SysMetricDlgProc(HWND, UINT, WPARAM, LPARAM);





Here is the code for the Dilaog proc, SysMetricDlgProc

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

{

    int wmId, wmEvent;



    switch (uMessage)

    {

        case WM_INITDIALOG:

            {

                // Create a Done button and size it. 

                SHINITDLGINFO shidi;

                SHMENUBARINFO mbi;



                shidi.dwMask = SHIDIM_FLAGS;

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

                shidi.hDlg = hDlg;

                SHInitDialog(&shidi);





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

                mbi.cbSize     = sizeof(SHMENUBARINFO);

                mbi.hwndParent = hDlg;

                mbi.nToolBarId = IDR_MENU_SYSMETRIC;

                mbi.hInstRes   = g_hInst;



                if (!SHCreateMenuBar(&mbi))

                {

                    printf("SHCreateMenuBar failed err code:%d\n", GetLastError());

                }

                else

                {

                }



                UpdateSystemMetric(hDlg);

            }

            return TRUE;



        case WM_COMMAND:

            wmId = LOWORD(wParam);

            wmEvent = HIWORD(wParam);



            switch(wmId)

            {

                case IDOK:

                    EndDialog(hDlg, uMessage);

                    return TRUE;



                case IDM_REFRESH:

                    //MessageBox(NULL, L"Pressed refresh",L"Info", MB_OK);

                    UpdateSystemMetric(hDlg);

                    return TRUE;



                case IDM_BACK:

                    EndDialog(hDlg, uMessage);

                    return TRUE;

            }

            break;



        case WM_CLOSE:

            EndDialog(hDlg, uMessage);

            return TRUE;



    }



    return FALSE;

}





In WM_INITDIALOG, we initialise the dialog as full screen and also add the menu bar. The UpdateSystemMetric() function updates the dialog with the system width and system height.


BOOL UpdateSystemMetric(HWND hDlg)

{

    int width = -1, height = -1;



    width =  GetSystemMetrics(SM_CXSCREEN);

    height = GetSystemMetrics(SM_CYSCREEN);



    SetDlgItemInt(hDlg, IDC_STATIC_SYSWIDTH, width, FALSE);

    SetDlgItemInt(hDlg, IDC_STATIC_SYSHEIGHT, height, FALSE);



    return TRUE;

}

UpdateSystemMetric() function uses the same api to get the screen width and height. And it uses SetDlgItemInt() api to set the contents of IDC_STATIC_SYSWIDTH static control to width, and the same for height.





Now we handle IDM_BACK and IDM_REFRESH under WM_COMMAND. IDM_BACK simply ends the dialog and returns to the main screen, IDM_REFRESH, re-calculates the width and height and updates the values in the UI.





Now when we select the "System Metric" menu item, the dialog box will show up as below:





and when we switch the emulator to landscape mode and hit "Refresh", the values are updated in the UI.

Part 1
Part 2

Applications: Adding Image to your application

Well, back from work and I was sitting idle. I thought the first screen of my application looks a bit stale really, so, I decided to add a small picture to the main screen.

 

Adding an image resource to your project:
This is really easy.

 

1. Copy the bitmap file into you project directory.
2. Go to Resource View, right click and select "Add Resource.."
3. Select "Bitmap" and click on "Import.."
4. Point it to the file you copied in step 1
5. Note the ID assigned to your bitmap. In my case it was IDB_BITMAP1

 

And thats it! The resource is now present within your project, you just need to write code to use it.

 

In my WndProc function, just before breaking under WM_CREATE, I make a call to this function AlignComponents(), which looks like this:

void AlignComponents(HWND hWnd)

{

    RECT rect;

    int imageWidth = 32, imageHeight = 32;

    int imageX = 0, imageY = 0;

    int captionX = 0, captionY = 0;

    const int offset = 25;

    TCHAR captionText[] = TEXT("TechTwaddle\n Copyright (c) 2009");



    GetClientRect(hWnd, &rect);



    imageX = rect.right/2 – imageWidth/2;

    imageY = rect.bottom/2 – imageHeight*2;

   

    HWND hStatic = CreateWindowEx (0, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | SS_BITMAP,

                                    imageX, imageY, imageWidth, imageHeight, hWnd, 0, g_hInst, NULL);



    if (hStatic)

    {

        HBITMAP hImage = LoadBitmap(g_hInst, MAKEINTRESOURCE(IDB_BITMAP1));

        SendMessage(hStatic, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hImage);

    }



    captionX = rect.left + offset;

    captionY = imageY + imageHeight + 3;



    HWND hStaticCaption = CreateWindowEx(0, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | SS_CENTER,

                                    captionX, captionY, rect.right – offset*2, 30, hWnd, 0, g_hInst, NULL);



    if(hStaticCaption)

    {

        SendMessage(hStaticCaption, WM_SETTEXT, 0, (LPARAM)captionText);

    }

}

The above code gets the client rect of the main window and calculates the X and Y position of the image based on the rect. I then use CreateWindowEx to create a static control which will show my bitmap. Use LoadBitmap to load the bitmap and get a handle to it and then send STM_SETIMAGE message, passing the bitmap handle so that the image can be shown in the control. Note that the static control was created using SS_BITMAP as one of its styles. Without this style the static control will not show the image.


Then I create another static control positionally relative to the image. And send WM_SETTEXT message to it to set the text. Note that the SS_BITMAP style is not mentioned in this static control. I had just copy pasted the code from above with SS_BITMAP set and sat wondering for a few minutes why the text was not showing up.
 

Anyways, my main screen now looks like:

A lot better than before. I thought I’ll change the font of the text but was too lazy. Maybe tomorrow.

Applications: That thing called Gradience

I added a little gradient fill to the main screen. And it is quite easy really. By the way, here’s what my main screen looks like now, cool eh (:

Lets look at GradientFill first. The function is prototyped as:

 

BOOL GradientFill(HDC hdc, PTRIVERTEX pVertex, ULONG numVertex, PVOID pMesh, ULONG numMesh, ULONG mode);

 

The first parameter is the handle to the device context. The second parameter, pVertex, is a pointer to an array of TRIVERTEX structures. The third parameter contains the number of elements in the array pointed to by pVertex.

TRIVERTEX structure:

struct _TRIVERTEX

{

    LONG x;

    LONG y;

    COLOR16 Red;

    COLOR16 Green;

    COLOR16 Blue;

    COLOR16 Alpha;

} TRIVERTEX;

The structure defines a point x,y in the device context and its color. Alpha define the transparency of the point x,y. The points in the array should define the upper left and the lower right corners of the rectangle being filled. The fourth parameter pMesh points to a GRADIENT_RECT structure.

struct _GRADIENT_RECT

{

    ULONG topLeft;

    ULONG bottomRight;

} GRADIENT_RECT;

The GRADIENT_RECT structure specifies which entry in the array containing TRIVERTEX array is the upper left point and which is the bottom right point. The last parameter, mode, specifies whether the gradient should be filled horizontally, GRADIENT_FILL_RECT_H, or vertically GRADIENT_FILL_RECT_V.

 

Now to the static text control. I tried to make the control transparent but found out that it is not very straight forward. Tried to use SetBkMode(hdc, TRANSPARENT) and reurning NULL_BRUSH in response to WM_CTLCOLORSTATIC, didn’t work for me. So I removed the static control and used ExtTextOut to draw the text directly on the screen.

 

Here is the final piece of code. All the code goes in under WM_PAINT function. And g_hImageStatic is a global which contains window handle to the static control containing the image. I store it while creating the static control in AlignComponents(), check my previous post.

TRIVERTEX vert[2];

GRADIENT_RECT gradRect;

RECT rect;

TCHAR szStrLine1[] = TEXT("TechTwaddle");

TCHAR szStrLine2[] = TEXT("Copyright (c) 2009");



hdc = BeginPaint(hWnd, &ps);



GetClientRect(hWnd, &rect);



vert[0].x = rect.left;

vert[0].y = rect.top;

vert[0].Red = 0x0000;

vert[0].Green = 0x2000;

vert[0].Blue = 0xA000;

vert[0].Alpha = 0x0000;



vert[1].x = rect.right;

vert[1].y = rect.bottom;

vert[1].Red = 0x0000;

vert[1].Green = 0x8000;

vert[1].Blue = 0xFF00;

vert[1].Alpha = 0x0000;



gradRect.UpperLeft = 0;

gradRect.LowerRight = 1;



GradientFill(hdc, vert, 2, &gradRect, 1, GRADIENT_FILL_RECT_H);



if(g_hImageStatic)

{

    GetWindowRect(g_hImageStatic, &rect);



    SetTextColor(hdc, RGB(120, 160, 130));



    SetBkMode(hdc, TRANSPARENT);

    ExtTextOut(hdc, rect.left – 23, rect.bottom – 24, 0, NULL,

        szStrLine1, _tcslen(szStrLine1), NULL);

   

    ExtTextOut(hdc, rect.left – 37, rect.bottom – 11, 0, NULL,

        szStrLine2, _tcslen(szStrLine2), NULL);

}



EndPaint(hWnd, &ps);