下面,我按照自己的理解将这个例子详细分析。
这个例子属于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");
后面是函数声明和全局变量:
- // function declarations
- 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();
- // global variables
- 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;
- // Save the instance handle so we can access it later
- g_hInstance = hInstance;
- // Check to see if this application is already running
- hEvent = FindPrevInstance();
- if (hEvent == NULL)
- {
- // We found another instance
- return -1;
- }
- // Create our main application window
- hwnd = CreateMainWindow(nShowCmd);
- if (hwnd == NULL)
- {
- // Failed to initialize
- CloseHandle(hEvent);
- return GetLastError();
- }
- // Run the message loop (note that GetMessage() returns -1 for error)
- while (GetMessage(&msg, NULL, 0, 0) > 0)
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- // Clean up
- FreePaintImage();
- FreePaintFont();
- // This is the value we passed to PostQuitMessage()
- CloseHandle(hEvent);
- return msg.wParam;
- }
代码本身的注释应该算比较详细了。简单说明一下。先是判断一下该程序是否已经有一个在运行,如果有,则不再运行。这是为了保证该应用在一台设备上一次只能运行一个。如果你做的程序有需要可以同时运行多个的话,就不要加上这样的处理。个人建议,为了安全有效,还是做这样的限制吧。然后用CreateMainWindow(nShowCmd)来注册窗口,并创建该窗口。接下来的while循环用来处理消息。如果你看过windows SDK的资料应该不会陌生。FreePaintImage()和FreePaintFont()是用来施放已分配的资源。最后关闭句柄,返回消息参数。主流程就是这些,后面说明具体每个部分。 里面判断了两次,如果两次都找到该应用的另一个实例才退出。
先说明保证同时只有一个该应用运行的部分:
后面注册并创建主窗口:
- HWND CreateMainWindow(int nShowCmd)
- {
- WNDCLASS wc;
- ATOM atm;
- TCHAR szTitle[128];
- HWND hwnd;
- // Set up the window class description
- ZeroMemory(&wc, sizeof(wc));
- wc.lpfnWndProc = MainWindowProc; // 设置该窗口的回调函数,用于处理消息
- wc.hInstance = g_hInstance;
- wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
- wc.lpszClassName = g_szClassName;
- // We want to redraw the window contents anytime we get resized. That way
- // we'll respond appropriately when the user switches between portrait and
- // landscape. If we had any child windows or controls, we'd need to
- // reposition or resize them when we get a WM_SIZE message.
- wc.style = CS_HREDRAW | CS_VREDRAW;
- // Register the window class
- atm = RegisterClass(&wc); // 注册窗口类
- if (atm == 0)
- {
- // Failed!!
- return NULL;
- }
- // Load the window title string resource
- if (!LoadString(g_hInstance, IDS_APPTITLE, szTitle, ARRAYSIZE(szTitle)))
- {
- // Failed!!
- return NULL;
- }
- // Create a window using the class we just registered. Note that the
- // initial size and position don't matter, because we're going to make it
- // fullscreen when we get WM_CREATE, before it's ever displayed.
- 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)
- {
- // Failed!!
- return NULL;
- }
- // Make the window visible and paint before returning
- ShowWindow(hwnd, nShowCmd);
- UpdateWindow(hwnd);
- return hwnd;
- }
下面设置窗口类型和风格:
- BOOL OnCreateMainWindow(HWND hwnd)
- {
- SHMENUBARINFO shmbi;
- SHINITDLGINFO shidi;
- // Create our softkey bar
- 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))
- {
- // Failed!!
- return FALSE;
- }
- // Windows Mobile applications should always display their main window
- // full-screen. We're going to let the OS do this for us by calling
- // SHInitDialog, even though technically this window isn't a dialog window.
- shidi.dwMask = SHIDIM_FLAGS;
- shidi.hDlg = hwnd;
- shidi.dwFlags = SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_SIPDOWN;
- if (!SHInitDialog(&shidi))
- {
- // Failed!!
- return FALSE;
- }
- // Get the current user preference for text size
- SHGetUIMetrics(SHUIM_FONTSIZE_PIXEL,
- &g_dwFontSize, sizeof(g_dwFontSize), NULL);
- // Success
- 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;
- // Start the paint operation
- if (!GetUpdateRect(hwnd, NULL, FALSE) ||
- BeginPaint(hwnd, &ps) == NULL)
- {
- return;
- }
- // Load the correct image and get its label
- hBitmap = LoadPaintImage(szLabel, ARRAYSIZE(szLabel));
- if (hBitmap != NULL)
- {
- // Load the display font
- hFont = LoadPaintFont();
- if (hFont != NULL)
- {
- // Save the device context state
- nSavedDC = SaveDC(ps.hdc);
- // Calculate the height of our display image and text
- 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);
- // Draw the image
- 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);
- }
- // Draw the text
- rect.top = (rect.bottom - cy) / 2 + bm.bmHeight;
- DrawText(ps.hdc, szLabel, -1, &rect,
- DT_CENTER | DT_NOPREFIX | DT_WORDBREAK);
- // Restore the device context
- RestoreDC(ps.hdc, nSavedDC);
- }
- }
- // Always need to finish!!
- EndPaint(hwnd, &ps);
- }
消息处理的回调函数:
- LRESULT CALLBACK MainWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
- {
- static SHACTIVATEINFO sai;
- switch (msg)
- {
- case WM_CREATE:
- // Initialize the static window state information. The shell helper functions
- // will use this buffer to store their state.
- ZeroMemory(&sai, sizeof(sai));
- sai.cbSize = sizeof(sai);
- // Initialize the window (if we fail, return -1)
- return (OnCreateMainWindow(hwnd) ? 0 : -1);
- case WM_ACTIVATE:
- // Calling this function when we get WM_ACTIVATE ensures that our
- // application will handle the SIP properly. This function does
- // nothing when we're running on Smartphone.
- SHHandleWMActivate(hwnd, wp, lp, &sai, 0);
- break;
- case WM_SETTINGCHANGE:
- // This helper function will resize our main application window when the SIP
- // goes up and down. Try commenting out this function and see how it affects
- // our drawing code. This function is optional, so choose whichever behavior
- // you prefer. Again, this function does nothing on Smartphone.
- SHHandleWMSettingChange(hwnd, wp, lp, &sai);
- break;
- case WM_HIBERNATE:
- // We get this message when the device is running low on memory. All
- // applications should free any memory and resources that they can. We'll
- // free our cached bitmap and font, since we can just re-create them the next
- // time we need them.
- FreePaintImage();
- FreePaintFont();
- return TRUE;
- case WM_COMMAND:
- if (LOWORD(wp) == ID_SWITCH)
- {
- // Switch to our next image
- g_dwCurrentImage = !g_dwCurrentImage;
- FreePaintImage();
- InvalidateRect(hwnd, NULL, TRUE);
- }
- break;
- case WM_KEYDOWN:
- // Allow Ctrl+Q to quit the application. Most users won't ever see
- // this, but it's handy for debugging.
- if (wp == 'Q' &&
- GetKeyState(VK_CONTROL) < 0)
- {
- SendMessage(hwnd, WM_CLOSE, 0, 0);
- }
- break;
- case WM_PAINT:
- PaintMainWindow(hwnd);
- return 0;
- case WM_DESTROY:
- // When this window is destroyed, it's time to quit the application
- PostQuitMessage(0);
- break;
- }
- if (msg == g_uMsgMetricChange)
- {
- DWORD dwFontSize;
- // The UI metrics have changed. This is how we get notified when the
- // user changes their preferred font size (PPC only). We free our
- // cached font and save the new size for the next time we paint.
- 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)
- {
- // Load the current label string
- pszLabel[0] = 0;
- LoadString(g_hInstance, IDS_LABEL1 + g_dwCurrentImage,
- pszLabel, cchLabel);
- // Load the current image
- if (g_hBitmapPaint == NULL)
- {
- // Note that the resource type must be "GIF", but actually the resource
- // data can contain a BMP, GIF, JPG, or PNG image. This enables us to
- // pick the best image format for our purposes (quality, size, etc).
- g_hBitmapPaint = SHLoadImageResource(g_hInstance,
- IDR_IMAGE1 + g_dwCurrentImage);
- }
- // Return the cached image handle
- return g_hBitmapPaint;
- }
加载字体的函数:
- HFONT LoadPaintFont()
- {
- LOGFONT lf;
- if (g_hFontPaint == NULL)
- {
- // Create the font handle
- 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 the cached font handle
- 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;
- }
- }