Chinaunix首页 | 论坛 | 博客
  • 博客访问: 16497966
  • 博文数量: 5645
  • 博客积分: 9880
  • 博客等级: 中将
  • 技术积分: 68081
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-28 13:35
文章分类

全部博文(5645)

文章存档

2008年(5645)

我的朋友

分类:

2008-04-28 20:58:13

下载本文示例代码
  介绍   很多人认为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成员函数作为线程函数。 共4页。 1 2 3 4 :   介绍   很多人认为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成员函数作为线程函数。 共4页。 1 2 3 4 : 下载本文示例代码


ATL布幔下的秘密之窗口类的秘密ATL布幔下的秘密之窗口类的秘密ATL布幔下的秘密之窗口类的秘密ATL布幔下的秘密之窗口类的秘密ATL布幔下的秘密之窗口类的秘密ATL布幔下的秘密之窗口类的秘密ATL布幔下的秘密之窗口类的秘密ATL布幔下的秘密之窗口类的秘密ATL布幔下的秘密之窗口类的秘密ATL布幔下的秘密之窗口类的秘密ATL布幔下的秘密之窗口类的秘密ATL布幔下的秘密之窗口类的秘密ATL布幔下的秘密之窗口类的秘密ATL布幔下的秘密之窗口类的秘密ATL布幔下的秘密之窗口类的秘密
阅读(99) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~