科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件ATL布幔下的秘密之窗口类的秘密

ATL布幔下的秘密之窗口类的秘密

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

很多人认为ATL只是用来编写COM组件的,其实你也可以使用ATL中的窗口类来创建基于窗口的应用程序 .

作者:李马编译 来源:VCKBASE 2007年10月19日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
下面就是使用了ZWindow类的更新程序。

  程序67.

#include <windows.h>

class ZWindow
{
 public:
  HWND m_hWnd;

  ZWindow(HWND hWnd = 0) : m_hWnd(hWnd) { }

  inline void Attach(HWND hWnd)
  { m_hWnd = hWnd; }

  inline BOOL ShowWindow(int nCmdShow)
  { return ::ShowWindow(m_hWnd, nCmdShow); }

  inline BOOL UpdateWindow()
  { return ::UpdateWindow(m_hWnd); }

  LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  {
   switch (uMsg)
   {
    case WM_DESTROY:
     PostQuitMessage(0);
     break;
   }
   return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
  }
};

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
 char szAppName[] = "Hello world";
 HWND hWnd;
 MSG msg;
 WNDCLASS wnd;
 ZWindow zwnd;

 wnd.cbClsExtra = NULL;
 wnd.cbWndExtra = NULL;
 wnd.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
 wnd.hCursor = LoadCursor(NULL, IDC_ARROW);
 wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION);
 wnd.hInstance = hInstance;
 wnd.lpfnWndProc = ZWindow::WndProc;
 wnd.lpszClassName = szAppName;
 wnd.lpszMenuName = NULL;
 wnd.style = CS_HREDRAW | CS_VREDRAW;

 if (!RegisterClass(&wnd))
 {
  MessageBox(NULL, "Can not register window class", "Error", MB_OK | MB_ICONINFORMATION);
  return -1;
 }

 hWnd = CreateWindow(szAppName, "Hello world", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

 zwnd.Attach(hWnd);

 zwnd.ShowWindow(nCmdShow);
 zwnd.UpdateWindow();

 while (GetMessage(&msg, NULL, 0, 0))
 {
  DispatchMessage(&msg);
 }

 return msg.wParam;
}

  这个程序只是简单示范了一下ZWindow的用法,说实话,这个类就不会做什么特别的了。它只是对Windows API的一层包装,唯一的优点就是你不需要传递HWND参数了,但是你必须得在调用成员函数的时候输入对象的名称。

  对于以前,你这样调用函数: ShowWindow(hWnd, nCmdShow);

  现在,你可以这么做: zwnd.ShowWindow(nCmdShow);

  到现在为止,这并不是一个明显的优点。

  我们来看看在WndProc中如何处理窗口消息。在前一个程序中,我们只处理了一个函数,也就是WM_DESTROY。如果你想要处理更多的消息,那么可以在switch语句中加入更多的case。让我们来修改一下WndProc,处理一下WM_PAINT。就像这个样子:

switch (uMsg)
{
 case WM_PAINT:
  hDC = ::BeginPaint(hWnd, &ps);
  ::GetClientRect(hWnd, &rect);
  ::DrawText(hDC, "Hello world", -1, &rect, DT_CENTER | DT_VCENTER DT_SINGLELINE);
  ::EndPaint(hWnd, &ps);
  break;
 case WM_DESTROY:
  ::PostQuitMessage(0);
  break;
}

  这个代码很正确,它会在窗口的正中显示Hello world。但是,为什么要用BeginPaint、GetClientRect和EndPaint这些API呢?根据我们的标准,这些API都应该作为ZWindow的成员函数来使用的——它们的第一个参数都是HWND。

  因为所有这些函数都是非static函数。并且,你不能在static成员函数中调用非static成员函数。为什么呢?因为它们的区别就是this指针,非static成员函数拥有this指针,而static函数没有。如果我们通过某种手段将this指针传递给了static成员函数,那么我们就可以在static成员函数中调用非static成员函数了。让我们看看下面的程序。

  程序68.

#include <iostream>
using namespace std;

class C
{
 public:
  void NonStaticFunc()
  {
   cout << "NonStaticFun" << endl;
  }

  static void StaticFun(C* pC)
  {
   cout << "StaticFun" << endl;
   pC->NonStaticFunc();
  }
};

int main()
{
 C objC;
 C::StaticFun(&objC);
 return 0;
}

  程序的输出为:

StaticFun
NonStaticFun

  所以,我们就可以使用和这里相同的技术,也就是将ZWindow对象的地址存入一个全局变量,然后利用这个指针调用非static成员函数。下面是前一个程序的更新版本,在其中我们没有直接调用窗口的API。

  程序69.

#include <windows.h>

class ZWindow;

ZWindow* g_pWnd = NULL;

class ZWindow
{
 public:
  HWND m_hWnd;

  ZWindow(HWND hWnd = 0) : m_hWnd(hWnd) { }

  inline void Attach(HWND hWnd)
  { m_hWnd = hWnd; }

  inline BOOL ShowWindow(int nCmdShow)
  { return ::ShowWindow(m_hWnd, nCmdShow); }

  inline BOOL UpdateWindow()
  { return ::UpdateWindow(m_hWnd); }

  inline HDC BeginPaint(LPPAINTSTRUCT ps)
  { return ::BeginPaint(m_hWnd, ps); }

  inline BOOL EndPaint(LPPAINTSTRUCT ps)
  { return ::EndPaint(m_hWnd, ps); }

  inline BOOL GetClientRect(LPRECT rect)
  { return ::GetClientRect(m_hWnd, rect); }

  BOOL Create(LPCTSTR szClassName, LPCTSTR szTitle, HINSTANCE hInstance,
HWND hWndParent = 0, DWORD dwStyle = WS_OVERLAPPEDWINDOW,
DWORD dwExStyle = 0, HMENU hMenu = 0)
 {
  m_hWnd = ::CreateWindowEx(dwExStyle, szClassName, szTitle, dwStyle,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, hWndParent, hMenu, hInstance, NULL);

  return m_hWnd != NULL;
 }

 static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
  ZWindow* pThis = g_pWnd;
  HDC hDC;
  PAINTSTRUCT ps;
  RECT rect;

  switch (uMsg)
  {
   case WM_PAINT:
    hDC = pThis->BeginPaint(&ps);
    pThis->GetClientRect(&rect);
    ::DrawText(hDC, "Hello world", -1, &rect,
    DT_CENTER | DT_VCENTER | DT_SINGLELINE);
    pThis->EndPaint(&ps);
    break;

   case WM_DESTROY:
    ::PostQuitMessage(0);
    break;
  }

  return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
 }
};

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
 char szAppName[] = "Hello world";
 MSG msg;
 WNDCLASS wnd;
 ZWindow zwnd;

 wnd.cbClsExtra = NULL;
 wnd.cbWndExtra = NULL;
 wnd.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
 wnd.hCursor = LoadCursor(NULL, IDC_ARROW);
 wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION);
 wnd.hInstance = hInstance;
 wnd.lpfnWndProc = zwnd.WndProc;
 wnd.lpszClassName = szAppName;
 wnd.lpszMenuName = NULL;
 wnd.style = CS_HREDRAW | CS_VREDRAW;

 if (!RegisterClass(&wnd))
 {
  MessageBox(NULL, "Can not register window class", "Error", MB_OK | MB_ICONINFORMATION);
  return -1;
 }

 g_pWnd = &zwnd;
 zwnd.Create(szAppName, "Hell world", hInstance);
 zwnd.ShowWindow(nCmdShow);
 zwnd.UpdateWindow();

 while (GetMessage(&msg, NULL, 0, 0))
 {
  DispatchMessage(&msg);
 }

 return msg.wParam;
}

  那么,我们终于有了这个可以工作的程序。现在,让我们来利用面向对象程序设计。如果我们对于每个消息都调用函数,并且使这些函数都成为虚函数的话,那么我们就可以在继承ZWindow类之后调用这些函数了。所以,我们可以自定义ZWindow的默认行为。现在,WndProc是类似这个样子:

static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam)
{
 ZWindow* pThis = g_pWnd;

 switch (uMsg)
 {
  case WM_CREATE:
   pThis->OnCreate(wParam, lParam);
   break;

  case WM_PAINT:
   pThis->OnPaint(wParam, lParam);
   break;

  case WM_DESTROY:
   ::PostQuitMessage(0);
   break;
 }
 return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}

  在这里,OnCreate和OnPaint是虚函数。并且,当我们从ZWindow继承一个类的时候,我们就可以重写所有我们想自定义的这些函数。
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章