首页 | 互联网 | IT动态 | IT培训 | Cisco | Windows | Linux | Java | .Net | Oracle | 软件测试 | C/C++ | 嵌入式开发 | 存储世界 | 服务器
网络设备 | IDC | 安全 | 求职招聘 | 数字网校 | 网页设计 | 平面设计 | 技术专题 | 电子书下载 | 教学视频 | 源码下载 | 搜索 | 博客 | 论坛
ASP | ASP.NET | JSP | PHP | AJAX | XML | Java script | HTML/CSS | 服务器类
各大城市软件开发培训、软件人才免费咨询热线:400-700-5807
 您现在的位置: 中国IT实验室 >> WEB开发 >> WEB开发 >> 正文
ATL布幔下的秘密之窗口类的秘密(1)
ChinaItLab  2005-1-6  保存本文    收藏本站


  介绍
  很多人认为ATL只是用来编写COM组件的,其实你也可以使用ATL中的窗口类来创建基于窗口的应用程序。虽然你可以将基于MFC的程序转换为ATL,但是ATL中对于UI(译注:用户界面)组件的支持太少了。所以,这就要求你需要自己编写很多代码。例如,在ATL中没有文档/视图,所以在你想使用它的时候就需要自己实现了。在本篇中,我们将要探究一些关于窗口类的秘密,以及ATL技术实现的秘密。WTL(Window Template Library,窗口模板库),虽然到现在(译注:本文于2002年10月27日发表在CodeProject)还不为Microsoft所支持,但是它在制作图形应用程序方面跨出了一大步。WTL就是基于ATL的窗口类的。
  
  在开始讨论基于ATL的程序之前,让我们从一个经典的Hello world程序开始吧。这个程序完全用SDK编写,并且我们中几乎所有人都已经熟悉它了。
  
  程序66.
  
  #include <windows.h>
  
  LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  
  int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  LPSTR lpCmdLine, int nCmdShow)
  {
   char szAppName[] = "Hello world";
   HWND hWnd;
   MSG msg;
   WNDCLASS wnd;
  
   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 = 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);
  
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);
  
   while (GetMessage(&msg, NULL, 0, 0))
   {
    DispatchMessage(&msg);
   }
   return msg.wParam;
  }
  
  LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  {
   HDC hDC;
   PAINTSTRUCT ps;
   RECT rect;
  
   switch (uMsg)
   {
    case WM_PAINT:
     hDC = BeginPaint(hWnd, &ps);
     GetClientRect(hWnd, &rect);
     DrawText(hDC, "Hello world", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
     EndPaint(hWnd, &ps);
     break;
  
    case WM_DESTROY:
     PostQuitMessage(0);
     break;
   }
   return DefWindowProc(hWnd, uMsg, wParam, lParam);
  }
  
  这个程序没有什么新鲜的东西,它就是显示了一个窗口,并在窗口中央显示Hello world。
  
  ATL是一个面向对象的开发库,也就是说你可以用类来完成工作。让我们尝试着自己来做一些相同的工作,编写一些微小的类来使我们的工作更加简单吧。好了,那我们来编写一些类来简化工作——但是编写这些类应该遵循一个什么样的标准呢?换句话说就是,需要编写多少类,它们的关系是什么,以及拥有什么样的方法和属性。在这里我并不打算讨论整个的面向对象理论,我们这里只是编写一个高质量的库。
  
  为了使我的任务相类似,我将相关的API进行了分组,并将这些相关的API放在了一个类里边。我将所有处理窗口的API放在了一个类里,并且它可以和其它的API相关联,例如字体、文件、菜单等等。所以我编写了一个很小的类,并将所有第一个参数为HWND的API放在了这个类中。也就是说,这个类只是简单地对窗口API进行了一层包装。我的类名称为ZWindow,当然你可以自由地选择你喜欢的名称。这个类是类似这个样子:
  
  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); }
  };
  
  在这里,我只封装了目前需要的API。你可以向这个类中添加全部的API。对于这个类来说的唯一优点,就是你不用像API那样传递HWND参数了,这个类本身会传递这个参数。
  
  呃,到现在为止还没有什么特别的。但是,我们的窗口回调函数怎么办呢?请记住,这个回调函数的第一个参数也是HWND,所以对于我们的标准而言,它也应该是这个类中的成员。所以,我也添加了我们的回调函数。现在,这个类就应该是类似这个样子了:
  
  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);
    }
  };
  
  你需要为WNDCLASS或WNDCLASSEX的一个域提供这个回调函数的地址。并且,你需要在创建ZWindow类对象之后像这样赋值:
  
  ZWindow zwnd;
  WNDCLASS wnd;
  
  wnd.lpfnWndProc = wnd.WndProc;
  
  但是当你编译程序的时候,编译器会给出类似这样的错误:
  
  cannot convert from ''long (__stdcall ZWindow::*)(struct HWND__ *,
  unsigned int,unsigned int,long)'' to ''long (__stdcall *)(struct HWND__ *,
  unsigned int, unsigned int,long)
  
  原因是你不能将成员函数作为回调函数来传递。为什么呢?因为在成员函数的情况下,编译器会自动传给成员函数一个参数,这个参数是指向这个类的指针,或者换句话说是this指针。所以这就意味着当你在成员函数中传递了n个参数的话,那么编译器会传递n+1个参数,并且那个附加的参数就是this指针。这条错误消息就表明编译器不能将成员函数转换为全局函数。
  
  那么,如果我们想将成员函数作为回调函数的话,应该怎么办呢?如果我们告诉编译器,不传递第一个this指针参数的话,那么我们就可以将成员函数作为回调函数了。在C++中,如果我们将成员函数声明为static的话,那么编译器就不会传递this指针了。这就是static和非static成员函数实质上的不同。
  
  所以,我们可以把ZWindow类中的WndProc声明为static成员函数。这一技术也可以用在多线程的情况下,比如当你想要使用成员函数作为一个线程函数的时候,你就可以将一个static成员函数作为线程函数。
  
  下面就是使用了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;
中国IT教育热线咨询
相关文章
Flash+PHP+Mysql简单留言本制作实例教程
基础知识:Java Web三层架构的配置详解
使用AJAX技术构建更优秀的Web应用程序
ASP应用程序设计的Web状态管理分析
Ajax驱动的Web站点
最新文章
·Ajax光环背后的隐患
·Windows操作系统下JSP程序开发环
·如何在Java程序中实现FTP的上传下
·Java应用:编写高级JavaScript应
·初学者学习java第一步——JDK环境
 文章评论

 精彩友情推荐
·Asp源码 PHP源码
·CGI源码 JSP源码
·建站书籍教程
·服务器软件 .net源码
·建站工具软件
·IDC资讯大全
·机房品质万里行
·IDC托管必备知识
·全国IDC报价
·网站推广优化
ASP.NET ASP PHP JSP
·ASP.NET开发中的八个最佳实践09-21
·ASP.NET开发中的验证码技术09-18
·控件开发asp.net处理标签间内容09-18
·程序员成为成为编程高手的二十二条军规09-18
·VS 2005和ASP.NET 2.0中处理CSS样式表09-17
·如何有效监控.NET应用程序09-16
·浅析ASP.NET 2.0 Client Callback09-16
·探讨ASP.NETMVC框架内置AJAX支持编程技术09-15
·ASP.NET2.0的URL映射的实现方法09-13
·Java\.net\PHP比较 程序员眼中的.Net世界09-12
·asp.net中实现观察者设计模式09-12
·谈JSP与XML的交互09-01
·ASP连接11种数据库语法总结09-01
·如何利用ASP实现邮箱访问09-01
·ASP随机数的应用技术09-01
·定时使用DWRUtil.addRow生成表格08-27
·ASP申请单动态添加实现方法及代码08-27
·关于ASP中脚本执行顺序的讲解08-25
·ASP程序实现网页伪静态页源代码08-25
·ASP如何调用webservice08-22
·ASP实例:读取xml文件的程序08-14
·Asp组件检测函数08-11
·Linux系统下让PHP提高性能的工具APC05-06
·一个完整、安全的PHP用户登录系统11-14
·Apache+PHP+MySQL建立数据库驱动的动态网站08-24
·用SSH与PHP相连接 确保数据传输的安全性08-23
·PHP5手动最简安装方法08-03
·PHP程序加速探索之服务器负载测试07-11
·完全讲解PHP+MySQL的分页显示示例分析05-30
·用Suhosin加强PHP脚本语言安全性05-26
·初学入门 PHP 和 MySQL05-17
·传奇的诞生 PHP三位创始人简介05-10
·大型系统上PHP令人不爽的九大原因05-10
·JSTL中fn表达式的使用说明09-18
·JSP Web框架研究:Struts09-16
·JSF在GlassFish管理控制台中的应用09-12
·介绍JSP程序动态网站环境搭建的详细步骤09-12
·Jsp页面中文参数传递get和post方法分析09-12
·Java\.net\PHP比较 程序员眼中的.Net世界09-11
·JavaScript最常用的55个经典技巧09-08
·Windows操作系统下JSP程序开发环境配置09-03
·JSF点滴积累--权限验证09-03
·小小分页帮助程序08-29
·经验分享全过程JSP程序员完全蜕变手册08-27
  培训中心
人才交流中心 技术交流中心
  ITLab技术交流平台: