D语言中国主页  D语言编辑器SciTE4D   DWin库 D语言官方网站
D语言编译器1.x最新版 OpenSource   Tango   webnews  Wiki

查看完整版本: 深入分析D语言接口与COM接口的关系

yidabu 2007-4-27 19:16

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

深入分析D语言接口与COM接口的关系知识若不分享 实在没有意义 http://www.d-programming-language-china.org 20070427点击下面网址查看原文:http://www.d-programming-language-china.org        by:        h_rain        from:        http://h-rain.javaeye.com/blog/73268        关键字:        D语言,COM,Interface        前两天为了解决dxpcom项目中遇到的xpcom接口兼容性问题,看了一下DMD编译器的源码,对D的接口有了一些了解,现在总结出来,备忘。        D中有了专门用于标识接口的关键字interface,而不用象C++中使用抽象类来代替。        D代码:        [Copy to clipboard] [ - ]CODE:                                interface ITest                {                int test();                }                C++代码:        [Copy to clipboard] [ - ]CODE:                                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)。( 本文出处: http://www.d-programming-language-china.org )        所以,出于简单实现的原则,DMD区分一个接口是D接口还是COM接口,关键就是判断这个接口是不是叫做IUnknown,以及这个接口是否继承自IUnknown,虽然接口都是通过Interface关键字声明。更有趣的是,DMD仅仅判断接口的名字是否为"IUnknown"而根本不管接口中的方法如何定义。        以上所述内容在进行Windows COM编程时,几乎不会被察觉,因为Windows的所有接口都是继承自IUnknown,只要正常使用就可以了。        而在进行Mozilla xpcom编程的时候,xpcom的根接口叫做ISupports,DMD根本就不会认为这是需要编译为C++兼容的COM接口,而仍然会将虚表的0项进行保留,结果给使用者造成了虚表指针偏移了的印象。( 本文出处: http://www.d-programming-language-china.org )        基于D的这个识别COM接口的方式,在dxpcom项目中,qiezi使用了别名的方式进行了变换,既将dxpcom项目中的所有的接口名称进行了优雅的统一,又能够使DMD生成正确的COM接口:        代码        [Copy to clipboard] [ - ]CODE:                                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指针无法被显示的调用,在执行的结果中就表现为虚表进行了偏移。        代码        [Copy to clipboard] [ - ]CODE:                                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!!( 本文出处: http://www.d-programming-language-china.org )        [Copy to clipboard] [ - ]CODE:                                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。( lastupdate:20070427 最新文章请访问http://www.d-programming-language-china.org )关于一大步成功社区:yidabu提倡在交流中学习,在分享中提高收集感兴趣的知识,写下心得,通过网络与别人一起分享理解一点就实践一步,收获什么就分享什么,成功就是这样一点点一步步累积起来的网络只是一个工具,只有自己身心提高才是实实在在的。d-programming-language-china.org为大家提供一个学习交流各种知识的平台
页: [1]
查看完整版本: 深入分析D语言接口与COM接口的关系