case WM_COMMAND:
{
wmID = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch(wmID)
{
case IDM_BUTTON_GMC:
{
SetWindowText(GetDlgItem(hDlg, IDC_STATIC_STATUS), L"In progress..");
/*
Do some work
*/
SetWindowText(GetDlgItem(hDlg, IDC_STATIC_STATUS), L"Done.");
}
break;
}
.
.
.
}
break;
Month: May 2009
Applications: Problems with painting DialogBox
BOOL CALLBACK DlgProc((HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
BOOL fRet = FALSE;
switch(uMessage)
{
case WM_INITDIALOG:
{
/*
Do SHInitDialog()
*/
/*
Do SHCreateMenuBar()
*/
/*
Setup controls in the dialog
*/
return TRUE;
}
break;
case WM_COMMAND:
{
/*
Handle all controls here
*/
return TRUE;
}
break;
case WM_ACTIVATE:
{
/*
Do some stuff
*/
}
break;
case WM_PAINT:
{
hdc = BeginPaint(hDlg, &ps);
EndPaint(hDlg, &ps);
}
break;
case WM_CLOSE:
{
EndDialog(hDlg, uMessage);
}
break;
}
fRet = TRUE;
return fRet;
}
Applications: Creating a simple UI application from scratch
Applications: Here is what I did over the weekend, Process Viewer
Plain text, ASCII, ANSI, UNICODE, UTF-8, UTF-16 confused?
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:
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
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
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;
}
HINSTANCE g_hInst;
HWND g_hWndMenuBar;
PROCESSENTRY32 ProcessList[MAX_PROCESSES];
#define MAX_PROCESSES 32
//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;
}
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 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;
}
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.
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;
}
ListView_SetItemState(hListView, 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
//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;
}
BOOL UpdateProcessView(HWND hDlg)
{
int procCount = 0;
procCount = GetRunningProcesses();
RefreshProcessListView(hDlg, procCount);
return TRUE;
}
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;
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