科技行者

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

知识库

知识库 安全导航

至顶网软件频道基础软件ATL布幔下的秘密之内部工作方式

ATL布幔下的秘密之内部工作方式

  • 扫一扫
    分享文章到微信

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

在本系列的教程中,我要讨论一些ATL的内部工作方式以及它所使用的技术。

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

关键字:

  • 评论
  • 分享微博
  • 分享邮件
程序15.

#include <iostream>
using namespace std;

class Base1 {
virtual void f() { cout << "Base1::f" << endl; }
virtual void g() { cout << "Base1::g" << endl; }
};

class Base2 {
virtual void f() { cout << "Base2::f" << endl; }
virtual void g() { cout << "Base2::g" << endl; }
};

class Base3 {
virtual void f() { cout << "Base3::f" << endl; }
virtual void g() { cout << "Base3::g" << endl; }
};

class Drive : public Base1, public Base2, public Base3 {
public:
virtual void fd() { cout << "Drive::fd" << endl; }
virtual void gd() { cout << "Drive::gd" << endl; }
};

typedef void(*Fun)(void);

int main() {
Drive objDrive;

Fun pFun = NULL;

// 调用Base1的第一个虚函数
pFun = (Fun)*((int*)*(int*)((int*)&objDrive+0)+0);
pFun();

// 调用Base1的第二个虚函数
pFun = (Fun)*((int*)*(int*)((int*)&objDrive+0)+1);
pFun();

// 调用Base2的第一个虚函数
pFun = (Fun)*((int*)*(int*)((int*)&objDrive+1)+0);
pFun();

// 调用Base2的第二个虚函数
pFun = (Fun)*((int*)*(int*)((int*)&objDrive+1)+1);
pFun();

// 调用Base3的第一个虚函数
pFun = (Fun)*((int*)*(int*)((int*)&objDrive+2)+0);
pFun();

// 调用Base3的第二个虚函数
pFun = (Fun)*((int*)*(int*)((int*)&objDrive+2)+1);
pFun();

// 调用派生类的第一个虚函数
pFun = (Fun)*((int*)*(int*)((int*)&objDrive+0)+2);
pFun();

// 调用派生类的第二个虚函数
pFun = (Fun)*((int*)*(int*)((int*)&objDrive+0)+3);
pFun();

return 0;
}

  程序的输出为:

Base1::f
Base1::g
Base2::f
Base2::f
Base3::f
Base3::f
Drive::fd
Drive::gd


  我们可以通过使用static_cast获得派生类虚函数表指针的偏移量,请看以下程序:

  程序16.

#include <iostream>
using namespace std;

class Base1 {
public:
virtual void f() { }
};

class Base2 {
public:
virtual void f() { }
};

class Base3 {
public:
virtual void f() { }
};

class Drive : public Base1, public Base2, public Base3 {
};

// 任意的非0值,因为0乘任何数都得0
#define SOME_VALUE 1

int main() {
cout << (DWORD)static_cast<Base1*>((Drive*)SOME_VALUE)-SOME_VALUE << endl;
cout << (DWORD)static_cast<Base2*>((Drive*)SOME_VALUE)-SOME_VALUE << endl;
cout << (DWORD)static_cast<Base3*>((Drive*)SOME_VALUE)-SOME_VALUE << endl;
return 0;
}

  ATL使用了一个定义在ATLDEF.H中的宏offsetofclass来做这件事,这个宏被定义为:

#define offsetofclass(base, derived) \
((DWORD)(static_cast<base*>((derived*)_ATL_PACKING))-_ATL_PACKING)

  这个宏返回了在派生类对象模型中基类虚函数表指针的偏移量,让我们来看看下面这个例子:

  程序17.

#include <windows.h>
#include <iostream>
using namespace std;

class Base1 {
 public:
  virtual void f() { }
};

class Base2 {
 public:
  virtual void f() { }
};

class Base3 {
 public:
  virtual void f() { }
};

class Drive : public Base1, public Base2, public Base3 {
};

#define _ATL_PACKING 8

#define offsetofclass(base, derived) \
((DWORD)(static_cast<base*>((derived*)_ATL_PACKING))-_ATL_PACKING)

int main() {
cout << offsetofclass(Base1, Drive) << endl;
cout << offsetofclass(Base2, Drive) << endl;
cout << offsetofclass(Base3, Drive) << endl;
return 0;
}

  派生类的内存布局为:


  程序的输出为:

0
4
8

  这个程序的输出示范了这个宏返回指定基类的虚函数表指针偏移量。在Don Box的《COM本质论》中,它使用了一个简单的的宏,你可以修改这个程序,用Box的宏替换ATL的宏。

  程序18.

#include <windows.h>
#include <iostream>
using namespace std;

class Base1 {
 public:
  virtual void f() { }
};

class Base2 {
 public:
  virtual void f() { }
};

class Base3 {
 public:
  virtual void f() { }
};

class Drive : public Base1, public Base2, public Base3 {
};

#define BASE_OFFSET(ClassName, BaseName) \
(DWORD(static_cast<BaseName*>(reinterpret_cast<ClassName*>\
(0x10000000))) - 0x10000000)

int main() {
 cout << BASE_OFFSET(Drive, Base1) << endl;
 cout << BASE_OFFSET(Drive, Base2) << endl;
 cout << BASE_OFFSET(Drive, Base3) << endl;
 return 0;
}

  这一程序的目的和输出与前一个程序完全相同。

  现在让我们用这个宏来做些特别的东西,事实上我们可以通过获得派生类内存结构中基类虚函数表指针的偏移量的方法来调用指定基类中的虚函数。

  程序19.

#include <windows.h>
#include <iostream>
using namespace std;

class Base1 {
 public:
  virtual void f() { cout << "Base1::f()" << endl; }
};

class Base2 {
 public:
  virtual void f() { cout << "Base2::f()" << endl; }
};

class Base3 {
 public:
  virtual void f() { cout << "Base3::f()" << endl; }
};

class Drive : public Base1, public Base2, public Base3 {
};

#define _ATL_PACKING 8

#define offsetofclass(base, derived) \
((DWORD)(static_cast<base*>((derived*)_ATL_PACKING))-_ATL_PACKING)

int main() {
 Drive d;

 void* pVoid = NULL;

 // 调用Base1的函数
 pVoid = (char*)&d + offsetofclass(Base1, Drive);
 ((Base1*)(pVoid))->f();

 // 调用Base2的函数
 pVoid = (char*)&d + offsetofclass(Base2, Drive);
 ((Base2*)(pVoid))->f();

 // 调用Base3的函数(译注:原文为Base1)
 pVoid = (char*)&d + offsetofclass(Base3, Drive);
 ((Base3*)(pVoid))->f();

 return 0;
}

  程序的输出为:

Base1::f()
Base2::f()
Base3::f()

  在本章教程中,我尝试着解释了ATL中offsetofclass宏的工作方式。我希望在下一篇文章中,继续探究ATL中其它的秘密。

查看本文来源

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

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

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