Better learn late than never (:
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:
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.
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;
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:
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)
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:
Check out this article by Dmitry Klionsky on code project where he talks about how to emulate Bluetooth on Windows Mobile Emulators:
http://www.codeproject.com/KB/mobile/bth4devemul.aspx
So there it is. A dumb application really. Let us add some functionality to it in the next post.
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
I_IMAGENONE, IDM_HELP, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_HELP, 0, 0,
I_IMAGENONE, IDM_MENU, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_MENU, 0, 0,
STRINGTABLE
BEGIN
.
.
IDS_HELP "HELP"
END
Change this to:
STRINGTABLE
BEGIN
.
.
IDS_MENU "Menu"
END
IDR_MENU_POPUP MENU DISCARDABLE
BEGIN
POPUP "Help"
BEGIN
MENUITEM "About", IDM_HELP_ABOUT
END
END
IDR_MENU_POPUP MENU DISCARDABLE
BEGIN
POPUP "Menu"
BEGIN
MENUITEM "About", IDM_HELP_ABOUT
END
END
IDR_MENU_POPUP MENU DISCARDABLE
BEGIN
POPUP "Menu"
BEGIN
MENUITEM "About", IDM_HELP_ABOUT
END
END
IDR_MENU_POPUP MENU DISCARDABLE
BEGIN
POPUP "Menu"
BEGIN
MENUITEM "System Metric", IDM_SYSTEM_METRIC
MENUITEM "About", IDM_HELP_ABOUT
END
END
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;