`
h_rain
  • 浏览: 120874 次
  • 性别: Icon_minigender_1
  • 来自: 哈尔滨
文章分类
社区版块
存档分类
最新评论

深入分析D语言接口与COM接口的关系

阅读更多
    前两天为了解决dxpcom项目中遇到的xpcom接口兼容性问题,看了一下DMD编译器的源码,对D的接口有了一些了解,现在总结出来,备忘。

    D中有了专门用于标识接口的关键字interface,而不用象C++中使用抽象类来代替。
interface ITest
{
int test();
}

class ITest
{
int test()=0;
}


    而D中的接口与C++中的接口不同之处是,D中的接口仍然含有ClassInfo,存放在虚表的0项上。

    从DMD的源码中可以得知,D中的类,接口都在虚表的0项上保存了ClassInfo指针。
    这样,D中的接口是无法与C++接口兼容的,则D就无法调用Windows的COM对象,至少是无法“优雅”的调用(仍然可以使用struct进行二进制兼容代替)。

    为了解决这个问题,DMD就需要能够表示出与C++兼容的COM接口,即需要一个虚表是"干净"的接口。又由于,从一个COM接口继承的接口仍然是一个COM接口,而COM模型的实现上又恰好定义了一个“IUnknown”根接口(COM体系中的所有的接口都是继承了IUnknown)。

    所以,出于简单实现的原则,DMD区分一个接口是D接口还是COM接口,关键就是判断这个接口是不是叫做IUnknown,以及这个接口是否继承自IUnknown,虽然接口都是通过Interface关键字声明。更有趣的是,DMD仅仅判断接口的名字是否为"IUnknown"而根本不管接口中的方法如何定义。

    以上所述内容在进行Windows COM编程时,几乎不会被察觉,因为Windows的所有接口都是继承自IUnknown,只要正常使用就可以了。

    而在进行Mozilla xpcom编程的时候,xpcom的根接口叫做ISupports,DMD根本就不会认为这是需要编译为C++兼容的COM接口,而仍然会将虚表的0项进行保留,结果给使用者造成了虚表指针偏移了的印象。

    基于D的这个识别COM接口的方式,在dxpcom项目中,qiezi使用了别名的方式进行了变换,既将dxpcom项目中的所有的接口名称进行了优雅的统一,又能够使DMD生成正确的COM接口:
extern(Windows)
interface IUnknown {
  static const char[] IID_STR = NS_ISUPPORTS_IID_STR;
  static const nsIID IID = NS_ISUPPORTS_IID;

  /* void QueryInterface (in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */
  nsresult QueryInterface(nsIID * uuid, void * *result);

  /* [noscript, notxpcom] nsrefcnt AddRef (); */
  nsrefcnt AddRef();

  /* [noscript, notxpcom] nsrefcnt Release (); */
  nsrefcnt Release();

}

alias IUnknown nsISupports;


   这个现象同时也很好的说明了,D中的别名(alias)在符号的处理方面仅仅是一个符号的替换,同C/C++中的#define的作用相同。

    下面的两段代码就能很好的诠释本文的内容(感谢qiezi提供)

    代码一,无法通过运行期断言,因为接口IInterface仍然为标准D接口,虚表的0项为ClassInfo指针无法被显示的调用,在执行的结果中就表现为虚表进行了偏移。
extern(Windows):   
int test1(IInterface p)   
{   
    return 1;   
}   
  
int test2(IInterface p)   
{   
    return 2;   
}   
  
int test3(IInterface p)   
{   
    return 3;   
}   
  
struct InterfaceVtbl   
{   
extern(Windows):   
    int function(IInterface) test1;   
    int function(IInterface) test2;   
    int function(IInterface) test3;   
}   
  
struct Interface   
{   
    InterfaceVtbl* vtbl;   
  
    InterfaceVtbl vtbl_;   
  
    static Interface opCall()   
    {   
        Interface res;   
        res.vtbl_.test1 = &test1;   
        res.vtbl_.test2 = &test2;   
        res.vtbl_.test3 = &test3;   
        res.vtbl = &res.vtbl_;   
        return res;   
    }   
}   
  
interface IInterface   
{   
    int test1();   
    int test2();   
    int test3();   
}   
  
extern (D):   
  
void main()   
{   
    Interface i = Interface();   
    assert(i.vtbl.test1(cast(IInterface)&i) == 1);   
    assert(i.vtbl.test2(cast(IInterface)&i) == 2);   
    assert(i.vtbl.test3(cast(IInterface)&i) == 3);   
  
    IInterface ii = cast(IInterface)&i;   
    assert(ii.test1() == 1);   
    assert(ii.test2() == 2);   
    assert(ii.test3() == 3);   
}


    代码二,与代码一的结构完全一致,却能够通过运行时断言的检查。唯一的不同仅仅是IInterface的名字换成了IUnknown!!
extern(Windows):   
int test1(IUnknown p)   
{   
    return 1;   
}   
  
int test2(IUnknown p)   
{   
    return 2;   
}   
  
int test3(IUnknown p)   
{   
    return 3;   
}   
  
struct InterfaceVtbl   
{   
extern(Windows):   
    int function(IUnknown) test1;   
    int function(IUnknown) test2;   
    int function(IUnknown) test3;   
}   
  
struct Interface   
{   
    InterfaceVtbl* vtbl;   
  
    InterfaceVtbl vtbl_;   
  
    static Interface opCall()   
    {   
        Interface res;   
        res.vtbl_.test1 = &test1;   
        res.vtbl_.test2 = &test2;   
        res.vtbl_.test3 = &test3;   
        res.vtbl = &res.vtbl_;   
        return res;   
    }   
}   
  
interface IUnknown   
{   
    int test1();   
    int test2();   
    int test3();   
}   
  
extern (D):   
  
void main()   
{   
    Interface i = Interface();   
    assert(i.vtbl.test1(cast(IUnknown)&i) == 1);   
    assert(i.vtbl.test2(cast(IUnknown)&i) == 2);   
    assert(i.vtbl.test3(cast(IUnknown)&i) == 3);   
  
    IUnknown ii = cast(IUnknown)&i;   
    assert(ii.test1() == 1);   
    assert(ii.test2() == 2);   
    assert(ii.test3() == 3);   
}


    另外需要说明的是extern(D),extern(Windows),extern(Pascal)等特征,只是用来描述函数的调用约定,与接口的类型无关。
    一句话:D中的类与标准D接口都有ClassInfo在虚表的0项上,而COM接口的虚表是干净的;而将一个接口声明为COM接口的方式为:将这个接口命名为IUnknown或继承自IUnknown。
分享到:
评论
5 楼 tomqyp 2007-10-05  
收藏~
4 楼 ideage 2007-04-23  
不错!好文章!
3 楼 qiezi 2007-04-23  
原以为要Hack DMD的,现在省了不少事。
2 楼 player7 2007-04-23  
不错,好方法.  这种重要的东西应该专门建文档保存呀
1 楼 oldrev 2007-04-22  
nice article!

相关推荐

    数据结构与算法分析_Java语言描述(第2版)]

    《数据结构与算法分析:Java语言描述(第2版)》把算法分析与最有效率的Java程序的开发有机地结合起来,深入分析每种算法,内容全面、缜密严格,并细致讲解精心构造程序的方法。目录: 译者序前言第1章 引论1.1 本书...

    数据结构与算法分析_Java语言描述(第2版)

    《数据结构与算法分析:Java语言描述(第2版)》把算法分析与最有效率的Java程序的开发有机地结合起来,深入分析每种算法,内容全面、缜密严格,并细致讲解精心构造程序的方法。 内容截图 目录: 译者序 前言 ...

    数据结构与算法分析 Java语言描述第2版

    《数据结构与算法分析:Java语言描述(第2版)》把算法分析与最有效率的Java程序的开发有机地结合起来,深入分析每种算法,内容全面、缜密严格,并细致讲解精心构造程序的方法。内容截图目录:译者序前言第1章 引论...

    深入解析ATL(第2版).pdf

     深入分析ATL实现COM内幕细节,展示COM应用中的各类漂亮技巧。  ATL的经典指南现已更新到ATL 8和VisuaI Studio 2005:  四位顶尖的Windows编程专家在本书中系统地揭示了ATL的内部工作原理,他们解释了ATL是 如何...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part2

    此外,本书的配套光盘还免费提供了价值人民币330元的java教学视频,对java语言进行了全面讲解,帮助一些不会java语言的读者快速地从java基础知识的学习中过渡到java web的学习与开发上. 第1部分 xml篇. 第1章 xml...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part4

    此外,本书的配套光盘还免费提供了价值人民币330元的java教学视频,对java语言进行了全面讲解,帮助一些不会java语言的读者快速地从java基础知识的学习中过渡到java web的学习与开发上. 第1部分 xml篇. 第1章 xml...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part3

    此外,本书的配套光盘还免费提供了价值人民币330元的java教学视频,对java语言进行了全面讲解,帮助一些不会java语言的读者快速地从java基础知识的学习中过渡到java web的学习与开发上. 第1部分 xml篇. 第1章 xml...

    JAVA WEB 开发详解:XML+XSLT+SERVLET+JSP 深入剖析与实例应用.part5

    此外,本书的配套光盘还免费提供了价值人民币330元的java教学视频,对java语言进行了全面讲解,帮助一些不会java语言的读者快速地从java基础知识的学习中过渡到java web的学习与开发上. 第1部分 xml篇. 第1章 xml...

    Thinking in Java 中文第四版+习题答案

    写在前面的话 引言 ...D.3.2 依赖语言的方法 D.3.3 特殊情况 D.4 参考资源 D.4.1 性能工具 D.4.2 Web站点 D.4.3 文章 D.4.4 Java专业书籍 D.4.5 一般书籍 附录E 关于垃圾收集的一些话 附录F 推荐读物

    Think in Java(中文版)chm格式

    引言 1. 前提 2. Java的学习 3. 目标 4. 联机文档 ...D.3.2 依赖语言的方法 D.3.3 特殊情况 D.4 参考资源 D.4.1 性能工具 D.4.2 Web站点 D.4.3 文章 D.4.4 Java专业书籍 D.4.5 一般书籍

    Java初学者入门教学

    1. 前提 2. Java的学习 3. 目标 4. 联机文档 5. 章节 6. 练习 ...D.3.2 依赖语言的方法 D.3.3 特殊情况 D.4 参考资源 D.4.1 性能工具 D.4.2 Web站点 D.4.3 文章 D.4.4 Java专业书籍 D.4.5 一般书籍

    JAVA_Thinking in Java

    引言 1. 前提 2. Java的学习 3. 目标 4. 联机文档 5. 章节 6. 练习 ...D.3.2 依赖语言的方法 D.3.3 特殊情况 D.4 参考资源 D.4.1 性能工具 D.4.2 Web站点 D.4.3 文章 D.4.4 Java专业书籍 D.4.5 一般书籍

    ThinkInJava

    . 前提 2. Java的学习 ...D.3.2 依赖语言的方法 D.3.3 特殊情况 D.4 参考资源 D.4.1 性能工具 D.4.2 Web站点 D.4.3 文章 D.4.4 Java专业书籍 D.4.5 一般书籍 附录E 关于垃圾收集的一些话 附录F 推荐读物

    java 编程入门思考

    引言 1. 前提 2. Java的学习 ...D.3.2 依赖语言的方法 D.3.3 特殊情况 D.4 参考资源 D.4.1 性能工具 D.4.2 Web站点 D.4.3 文章 D.4.4 Java专业书籍 D.4.5 一般书籍 附录E 关于垃圾收集的一些话 附录F 推荐读物

    thinkinjava

    引言 1. 前提 2. Java的学习 ...D.3.2 依赖语言的方法 D.3.3 特殊情况 D.4 参考资源 D.4.1 性能工具 D.4.2 Web站点 D.4.3 文章 D.4.4 Java专业书籍 D.4.5 一般书籍 附录E 关于垃圾收集的一些话 附录F 推荐读物

    Thinking in Java简体中文(全)

    引言 1. 前提 2. Java的学习 ...D.3.2 依赖语言的方法 D.3.3 特殊情况 D.4 参考资源 D.4.1 性能工具 D.4.2 Web站点 D.4.3 文章 D.4.4 Java专业书籍 D.4.5 一般书籍 附录E 关于垃圾收集的一些话 附录F 推荐读物

    java联想(中文)

    写在前面的话 ...D.3.2 依赖语言的方法 D.3.3 特殊情况 D.4 参考资源 D.4.1 性能工具 D.4.2 Web站点 D.4.3 文章 D.4.4 Java专业书籍 D.4.5 一般书籍 附录E 关于垃圾收集的一些话 附录F 推荐读物

    Thinking in Java(中文版 由yyc,spirit整理).chm

    D.3.2 依赖语言的方法 D.3.3 特殊情况 D.4 参考资源 D.4.1 性能工具 D.4.2 Web站点 D.4.3 文章 D.4.4 Java专业书籍 D.4.5 一般书籍 附录E 关于垃圾收集的一些话 附录F 推荐读物 英文版主页 | 中文版主页 | 详细...

    JAVA_Thinking in Java(中文版 由yyc,spirit整理).chm

    JAVA_Thinking in Java(中文版 由yyc...D.3.2 依赖语言的方法 D.3.3 特殊情况 D.4 参考资源 D.4.1 性能工具 D.4.2 Web站点 D.4.3 文章 D.4.4 Java专业书籍 D.4.5 一般书籍 附录E 关于垃圾收集的一些话 附录F 推荐读物

Global site tag (gtag.js) - Google Analytics