这个请求其实有点难度,Office开辟停止了。说来话巧,一天老O(Office项目标总工程师)和小B(VB项目标总工程师)一同饮酒,老O向小B倾吐了他的懊恼:
老O:怎样能让我写的程序C,能够挪用别的人写的程序S中的函数?(C暗示客户程序,S暗示供应服务的程序)
小B:你是否是喝懵懂了?让S作成DLL,你往LoadLibrary()、GetProcAddress()、...FreeLibrary()?!
老O:空话!如果这么复杂就行了。成绩是,连我都不晓得这个S程序是干甚么的?无能甚么?我怎样挪用呀?
小B:哦......这个对照初级,但我如今不克不及告知你,由于我怕你印象不深。
老O:~!·#¥%……—*......
小B:是如许的,在VB中,我们制订了一个尺度,这个尺度同意任何一个VB开辟者,把他本人写的某个功效的小程序放在VB的工具栏上,如许就好象他扩大了VB的功效一样。
老O:哦?就是谁人叫甚么VBX的滥玩艺儿?
小B:我呸......别看VBX这个工具不起眼儿,切实其实我也没看上它。但你猜怎样着?如今有不计其数的VB程序喜好者把他们写的形形色色功效的VBX小程序,放到网上,让人人共享那。
老O:哦~~~,那你们的这个VBX尺度是甚么?
小B:嘿嘿......实在特复杂,就是在VBX中必需完成7个函数,这7个函数称号和功效必需是:初始化、开释、显现、动静处置......,而至于它外部想干甚么,我也管不着。我只是在必要的时分挪用我必要的这7个函数。
老O:哦~~~,如许呀......对了,我现有个急事,我先走了。88,你付帐吧......
小B:喂!喂喂......走这么急干甚么,钱包都失落了:-)
老O固然丢了钱包,仍旧镇静地冲回办公室,他入手下手了思索......
1、我的程序C,要能挪用任何人写的程序B。那末B必需要依照我事前的请求,供应我必要的函数F1(),F2(),F3(),K1(),K2()。
2、BASIC是注释实行,因而它的函数不必思索誊写按次,只需给出函数名,注释器就可以找到。但我利用的是C++呀......
3、C++编译后的代码中没有函数名,只要函数地点,因而我必需改善为用VTAB(虚函数表)暗示函数出口:
<br>
、VTAB的布局
4、还不敷好,必要改善一下,由于一切的函数地点都放在一个表中会不天真、欠好修正、不容易扩大。恩,有了!依照函数功效的范例举行分类:
<br>
、多个VTAB的布局
5、成绩又来了,如今有2个VTAB虚函数表,那末怎样可以从一个表找到另外一个表那?恩又有举措了,我请求你必需要完成一个函数,而且这个函数地点必需放在一切表的开首(表中的第一个函数指针),这个函数就叫QueryInterface()吧,完成从一个表查找到另外一个表的功效:(除QueryInterface()函数,特地也完成别的两个函数,叫AddRef()和Release()。这两个函数的功效今后再说)
<br>
、COM接口布局
6、为了今后形貌便利,不再利用上图(图四)的办法了,而利用图五如许简便的款式:
<br>
、COM接口布局的简便图示
6、接口(Interface)观点
1、函数是经由过程VTAB虚函数表供应其地点,从另外一个角度来看,不论用甚么言语开辟,编译器发生的代码都能天生这个表。如许就完成了组件的“二进制特征”轻松完成了组件的跨言语请求。
2、假定有一个指针型变量保留着VTAB的首地点,则这个变量就叫“接口指针”(注6),变量定名的时分,习气上加上"I"开首。别的为了辨别分歧的接口,每一个接口也都要有一个名字,该名字就和CLSID一样,利用GUID体例,叫IID。
3、接口一经宣布,就不克不及再修正了。否则就会呈现向前兼容的成绩。这本性质叫“接口稳定性”。
4、组件中必需有3个函数,QueryInterface、AddRef、Release,它们3个函数也构成一个接口,叫"IUnknown"。(注7)
5、任何接口,实在都包括了IUnknown接口。跟着你打仗到更多的接口就会了更体味解到接口的另外一本性质“承继性”。
6、在任何接口上,挪用表中的第一个函数,实在就是挪用QueryInterface()函数,就失掉你想要的别的一个接口指针。这本性质叫“接口的传送性”
7、C/C++言语中必要事前对函数声明,那末就会请求组件也必需供应C言语的头文件。不可!为了能使COM具有跨言语的才能,决意不再为任何言语供应对应的函数接口声明,而是自力地供应一个叫范例库(TLB)的声明。每一个言语的IDE情况本人往依据TLB天生本人言语必要的包装。这本性质叫“接口声明的自力性”(注8)
7、客户程序与组件之间的协商挪用
回到我们的上一个话题,Word中嵌进一个组件,那末Word是怎样协商利用这个组件的那?上面是容器和组件之间的一个摹拟对话历程:
容器协商部分组件应对部分1依据CLSID启动组件。
CoCreateInstance()天生对象,实行机关函数,实行初始化举措。2你有IUnknown接口吗?有,给你!3恩,太好了,那末你有IPersistStorage接口吗?(注9)
IUnknown::QueryInterface(IID_IPersistStorage...)没有!4真低劣,连这个都没有。那你有IPersistStreamInit接口吗?(注10)
IUnknown::QueryInterface(IID_IPersistStreamInit...)哈,这个有,给!5好,好,这还差未几。你如今给我初始化吧。
IPersistStreamInit::InitNew()OK,初始化完成了。6完成了?好!如今你读数据往吧。
IPersistStreamInit::Load()读完啦。我依据数据,已在窗口中显现出来了。7好,如今我们各自处置用户的鼠标、键盘动静吧............8哎呀!用户要保留加入程序了。你的数据被用户修正了吗?
IPersistStreamInit::IsDirty()改了,用户已修正啦。9那好,那末用户修正后,你的数据必要多年夜的存储空间呀?
IPersistStreamInit::GetSizeMax()恩,我算算呀......好了,统共必要500KB。10晕,你这么个小玩意竟然占用这么年夜空间?!......好了,你能够存了。
IPersistStreamInit::Save()感谢,我已存好了。11恩。拜拜了您那。(注11)
IPersistStreamInit::Release();IUnknown::Release()实行析构函数,删除对象。12我本人也该加入了......
PostQuitMessage()