在WM6 SDK的通用例子中有一个basicapp的例子。运行的效果就是在WM上出现一个窗口,显示一幅图片,下面左软按键可以切换图片。例子的功能很简单,作为入门学习很好。
下面,我按照自己的理解将这个例子详细分析。
这个例子属于Native的方式,其含义是基本使用的是API编程,而不是基于其他框架开发。也可以叫做“原生态”开发方式。因此有自己的特殊宏定义:
- #define WIN32_LEAN_AND_MEAN
接下来就是包含的头文件声明:
- #include <windows.h>
- #include <aygshell.h>
- #include "resource.h"
第一个头文件很好理解,是为了处理基本类型及基本操作而用到,第三个头文件则包含的是一些资源相关的定义。第二个头文件需要好好了解一下用途。这里暂且跳过,不表。
下面定义了一个用于处理计算数组元素个数的宏:
- #define ARRAYSIZE(a) (UINT_PTR)(sizeof(a)/sizeof((a)[0]))
紧接着是应用的名称说明:
- static const TCHAR g_szClassName[] = TEXT("ClassName");
后面是函数声明和全局变量:
- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd);
- HANDLE FindPrevInstance();
- HWND CreateMainWindow(int nShowCmd);
- BOOL OnCreateMainWindow(HWND hwnd);
- void PaintMainWindow(HWND hwnd);
- LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
- HBITMAP LoadPaintImage(LPTSTR pszLabel, UINT_PTR cchLabel);
- HFONT LoadPaintFont();
- void FreePaintImage();
- void FreePaintFont();
- HINSTANCE g_hInstance;
- HBITMAP g_hBitmapPaint = NULL;
- HFONT g_hFontPaint = NULL;
- DWORD g_dwFontSize;
- DWORD g_dwCurrentImage = 0;
- UINT g_uMsgMetricChange = RegisterWindowMessage(SH_UIMETRIC_CHANGE);
好戏要开始了。
首先是程序的主函数,即程序的入口。如果是基于MFC、ATL或者是C#开发的话,一般是看不到这个冬冬的。这个是专门留给操作系统调用的。
- int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd)
- {
- HANDLE hEvent;
- HWND hwnd;
- MSG msg;
-
- g_hInstance = hInstance;
-
- hEvent = FindPrevInstance();
- if (hEvent == NULL)
- {
-
- return -1;
- }
-
- hwnd = CreateMainWindow(nShowCmd);
- if (hwnd == NULL)
- {
-
- CloseHandle(hEvent);
- return GetLastError();
- }
-
- while (GetMessage(&msg, NULL, 0, 0) > 0)
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-
- FreePaintImage();
- FreePaintFont();
-
- CloseHandle(hEvent);
- return msg.wParam;
- }
代码本身的注释应该算比较详细了。简单说明一下。先是判断一下该程序是否已经有一个在运行,如果有,则不再运行。这是为了保证该应用在一台设备上一次只能运行一个。如果你做的程序有需要可以同时运行多个的话,就不要加上这样的处理。个人建议,为了安全有效,还是做这样的限制吧。然后用CreateMainWindow(nShowCmd)来注册窗口,并创建该窗口。接下来的while循环用来处理消息。如果你看过windows SDK的资料应该不会陌生。FreePaintImage()和FreePaintFont()是用来施放已分配的资源。最后关闭句柄,返回消息参数。主流程就是这些,后面说明具体每个部分。
先说明保证同时只有一个该应用运行的部分:- HANDLE FindPrevInstance()
- {
- HANDLE hEvent;
- HWND hwnd;
- UINT cTries = 0;
-
- hEvent = CreateEvent(NULL, TRUE, FALSE, g_szClassName);
- if (hEvent != NULL)
- {
-
-
- if (GetLastError() == ERROR_ALREADY_EXISTS)
- {
- do
- {
-
- Sleep(cTries ? 250 : 0);
-
- hwnd = FindWindow(g_szClassName, NULL);
- if (hwnd != NULL)
- {
- SetForegroundWindow((HWND)((UINT_PTR)hwnd | 0x01));
- CloseHandle(hEvent);
- return NULL;
- }
- }
- while (++cTries < 2);
-
-
- }
- }
-
- return hEvent;
- }
里面判断了两次,如果两次都找到该应用的另一个实例才退出。SetForegroundWindow是为了将已经运行的该应用实例窗口切换到前台,并激活该窗口。
后面注册并创建主窗口:- HWND CreateMainWindow(int nShowCmd)
- {
- WNDCLASS wc;
- ATOM atm;
- TCHAR szTitle[128];
- HWND hwnd;
-
- ZeroMemory(&wc, sizeof(wc));
- wc.lpfnWndProc = MainWindowProc;
- wc.hInstance = g_hInstance;
- wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
- wc.lpszClassName = g_szClassName;
-
-
-
-
- wc.style = CS_HREDRAW | CS_VREDRAW;
-
- atm = RegisterClass(&wc);
- if (atm == 0)
- {
-
- return NULL;
- }
-
- if (!LoadString(g_hInstance, IDS_APPTITLE, szTitle, ARRAYSIZE(szTitle)))
- {
-
- return NULL;
- }
-
-
-
- hwnd = CreateWindow((LPCTSTR)atm, szTitle, WS_OVERLAPPED | WS_SYSMENU,
- CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
- NULL, NULL, g_hInstance, NULL);
- if (hwnd == NULL)
- {
-
- return NULL;
- }
-
- ShowWindow(hwnd, nShowCmd);
- UpdateWindow(hwnd);
- return hwnd;
- }
下面设置窗口类型和风格:- BOOL OnCreateMainWindow(HWND hwnd)
- {
- SHMENUBARINFO shmbi;
- SHINITDLGINFO shidi;
-
- ZeroMemory(&shmbi, sizeof(shmbi));
- shmbi.cbSize = sizeof(shmbi);
- shmbi.hwndParent = hwnd;
- shmbi.dwFlags = SHCMBF_HMENU;
- shmbi.nToolBarId = IDM_MAIN;
- shmbi.hInstRes = g_hInstance;
- if (!SHCreateMenuBar(&shmbi))
- {
-
- return FALSE;
- }
-
-
-
- shidi.dwMask = SHIDIM_FLAGS;
- shidi.hDlg = hwnd;
- shidi.dwFlags = SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_SIPDOWN;
- if (!SHInitDialog(&shidi))
- {
-
- return FALSE;
- }
-
- SHGetUIMetrics(SHUIM_FONTSIZE_PIXEL,
- &g_dwFontSize, sizeof(g_dwFontSize), NULL);
-
- return TRUE;
- }
接着绘制该窗口(设置字体,加载图片等):
- void PaintMainWindow(HWND hwnd)
- {
- PAINTSTRUCT ps;
- int nSavedDC;
- HBITMAP hBitmap, hOldBitmap;
- TCHAR szLabel[64];
- HFONT hFont;
- RECT rect;
- BITMAP bm;
- int cy;
- HDC hMemDC;
-
- if (!GetUpdateRect(hwnd, NULL, FALSE) ||
- BeginPaint(hwnd, &ps) == NULL)
- {
- return;
- }
-
- hBitmap = LoadPaintImage(szLabel, ARRAYSIZE(szLabel));
- if (hBitmap != NULL)
- {
-
- hFont = LoadPaintFont();
- if (hFont != NULL)
- {
-
- nSavedDC = SaveDC(ps.hdc);
-
- GetObject(hBitmap, sizeof(bm), &bm);
- cy = bm.bmHeight;
- SelectObject(ps.hdc, hFont);
- GetClientRect(hwnd, &rect);
- cy += DrawText(ps.hdc, szLabel, -1, &rect,
- DT_CALCRECT | DT_CENTER | DT_NOPREFIX | DT_WORDBREAK);
-
- GetClientRect(hwnd, &rect);
- hMemDC = CreateCompatibleDC(ps.hdc);
- if (hMemDC != NULL)
- {
- hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);
- BitBlt(ps.hdc, (rect.right - bm.bmWidth) / 2,
- (rect.bottom - cy) / 2, bm.bmWidth, bm.bmHeight,
- hMemDC, 0, 0, SRCCOPY);
- SelectObject(hMemDC, hOldBitmap);
- DeleteDC(hMemDC);
- }
-
- rect.top = (rect.bottom - cy) / 2 + bm.bmHeight;
- DrawText(ps.hdc, szLabel, -1, &rect,
- DT_CENTER | DT_NOPREFIX | DT_WORDBREAK);
-
- RestoreDC(ps.hdc, nSavedDC);
- }
- }
-
- EndPaint(hwnd, &ps);
- }
消息处理的回调函数:
- LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
- {
- static SHACTIVATEINFO sai;
- switch (msg)
- {
- case WM_CREATE:
-
-
- ZeroMemory(&sai, sizeof(sai));
- sai.cbSize = sizeof(sai);
-
- return (OnCreateMainWindow(hwnd) ? 0 : -1);
- case WM_ACTIVATE:
-
-
-
- SHHandleWMActivate(hwnd, wp, lp, &sai, 0);
- break;
- case WM_SETTINGCHANGE:
-
-
-
-
- SHHandleWMSettingChange(hwnd, wp, lp, &sai);
- break;
- case WM_HIBERNATE:
-
-
-
-
- FreePaintImage();
- FreePaintFont();
- return TRUE;
- case WM_COMMAND:
- if (LOWORD(wp) == ID_SWITCH)
- {
-
- g_dwCurrentImage = !g_dwCurrentImage;
- FreePaintImage();
- InvalidateRect(hwnd, NULL, TRUE);
- }
- break;
- case WM_KEYDOWN:
-
-
- if (wp == 'Q' &&
- GetKeyState(VK_CONTROL) < 0)
- {
- SendMessage(hwnd, WM_CLOSE, 0, 0);
- }
- break;
- case WM_PAINT:
- PaintMainWindow(hwnd);
- return 0;
- case WM_DESTROY:
-
- PostQuitMessage(0);
- break;
- }
- if (msg == g_uMsgMetricChange)
- {
- DWORD dwFontSize;
-
-
-
- if (SUCCEEDED(SHGetUIMetrics(SHUIM_FONTSIZE_PIXEL,
- &dwFontSize, sizeof(dwFontSize), NULL)) &&
- dwFontSize != g_dwFontSize)
- {
- g_dwFontSize = dwFontSize;
- FreePaintFont();
- InvalidateRect(hwnd, NULL, TRUE);
- }
- }
- return DefWindowProc(hwnd, msg, wp, lp);
- }
加载图片的函数:- HBITMAP LoadPaintImage(LPTSTR pszLabel, UINT_PTR cchLabel)
- {
-
- pszLabel[0] = 0;
- LoadString(g_hInstance, IDS_LABEL1 + g_dwCurrentImage,
- pszLabel, cchLabel);
-
- if (g_hBitmapPaint == NULL)
- {
-
-
-
- g_hBitmapPaint = SHLoadImageResource(g_hInstance,
- IDR_IMAGE1 + g_dwCurrentImage);
- }
-
- return g_hBitmapPaint;
- }
加载字体的函数:- HFONT LoadPaintFont()
- {
- LOGFONT lf;
- if (g_hFontPaint == NULL)
- {
-
- ZeroMemory(&lf, sizeof(lf));
- lf.lfHeight = -(LONG)g_dwFontSize;
- lf.lfWeight = FW_NORMAL;
- lf.lfCharSet = DEFAULT_CHARSET;
- lf.lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS;
- g_hFontPaint = CreateFontIndirect(&lf);
- }
-
- return g_hFontPaint;
- }
最后别忘了,释放图片和释放字体资源:
- void FreePaintImage()
- {
- if (g_hBitmapPaint != NULL)
- {
- DeleteObject(g_hBitmapPaint);
- g_hBitmapPaint = NULL;
- }
- }
- void FreePaintFont()
- {
- if (g_hFontPaint != NULL)
- {
- DeleteObject(g_hFontPaint);
- g_hFontPaint = NULL;
- }
- }