社区
ATL
帖子详情
如何为一个EXE程序开放接口
chuan014
2009-07-08 11:05:18
比如说,我开发了一个邮件管理器,我想为这个程序开放一些编程接口,让第三方通过我开放的接口,在他们程序中,也能控制我的邮件管理器。有什么好的办法吗
...全文
1258
12
打赏
收藏
如何为一个EXE程序开放接口
比如说,我开发了一个邮件管理器,我想为这个程序开放一些编程接口,让第三方通过我开放的接口,在他们程序中,也能控制我的邮件管理器。有什么好的办法吗
复制链接
扫一扫
分享
转发到动态
举报
写回复
配置赞助广告
用AI写文章
12 条
回复
切换为时间正序
请发表友善的回复…
发表回复
打赏红包
w29468
2009-07-24
打赏
举报
回复
土方法,命令行参数方式
z888c
2009-07-23
打赏
举报
回复
软件插件技术的原理与实现
计算机系统应用 2003年07期 李延春
摘要
基于插件的应用系统拥有良好的可扩充性、可定制性和可维护性。
引言
插件是近年来十分常见的一种技术。插件结构有助于编写有良好的扩充和定制功能的应用程序。许多软件甚至操作系统或其外壳程序都使用了这种技术,著名的使用插件机制的软件是Winamp, Winamp早期的成功虽然在于其快速的解码引擎,但在MP3播放器中能够保特长久的霸主地位。也正是由于内置了健全的插件功能后期的Winamp中增加的MIDI、 MOD、WAVE等音乐格式的播放功能完全是靠插件实现的。本文将论述插件技术的基本原理,并给出三种不同的实现插件系统的方法。最重要的部分则是插件与主程序之间的交互插件,一般是一个遵循了某些特定规则的DLL,而主程序将所有插件接口在内存中的地址传递给插件插件则根据这些地址来调用插件接口完成所需功能获取所需资源等。
插件系统的基本原理
插件的木质是在不修改程序主体的情况下对软件功能进行加强。当插件的接口被公开时任何人都可以自己制作插件来解决一些操作上的不便或增加一些功能。一个插件框架包括两个部分:主程序(host)和插件((plug-in)。主程序即是“包含”插件的程序。插件必须实现若干标准接口,由主程序在与插件通信时调用。编程实现方面包括两个部分:一部分是主体程序的插件处理机制,用来进行初始化每个插件的过程,并且管理好每个插件接口。另一部分是插件的接口函数定义,将所有的插件接口函数进行封装。以便开发者自由调用。
插件系统的开发
本文将通过一个摸拟的音频播放器(使用VC++ 6.0 )来介绍插件的三种实现方法
(1)普通的输出函数的DLL方式
(2)使用C++的多态性。
(3)使用COM类别(category)机制。
首先对此音频播放器作以下说明:①这不是一个真的播放器。当然也不能真地播放音频文件。②每个插件支持一种格式的音频文件如Wma、mp3等,通过增加插件可以使系统支持更多的音频格式。③插件接口简单,其功能实现只是弹出一个对话框表明哪个插件的哪个功能被调用了。制作这个播放器的真正目的是演示插件技术的原理和实现方法只要掌握了其原理和方法就完全可以开发出有用的插件系统
不管用什么手段实现插件和主程序之间的交互必须有一个协议,对于方法(1)这个协议是一系列的函数。这些函数由插件DLL引出由主程序调用。对于方法〔2)协议则是一个(或多个)基类通常是抽象类,插件需要构造一个类来继承此抽象类并实现其接口方法,再为主程序提供一个创建和销毁此实现类的对象的公共方法这个公共方法理所当然也应成为协议的一部分。对于方法(3)则是一个(或多个)COM接口插件是一个COM组件,它实现了这些接口,并注册到约定的组件类别tcomponent category)下。
一般音频播放器都有这样一些基本功能:装载音频文件(LoadFlle)、播放(Play)、暂停(Pause),停止((Stop)。我们的播放器将提供这四个功能,但主程序本身并不会直接实现这些功能而是调用插件的实现,上文已经说过。每个插件支持一种音频格式,所以每个插件的功能实现都是不同的。在主程序打开某个格式的音频文件时,根据文件扩展名来决定调用哪个插件的功能,主程序可以在启动时加载所有插件,也可以在打开文件时动态加载所需插件,甚至可以在启动时加载一部分常用的插件,而在需要时加载其余插件开发者可以有很高的自由度,现在我们来详细讨论三种实现方法,
3.1第一种方法
3.1.1插件的实现
我们创建一个动态链接库PIugl.dll,为了支持四个基本功能,它输出相应的四个函数
void LoadFile(const char* szHeName)
void Play()
void Pause()
void Stop()
这些函数可以简单实现为只弹出一个消息框,表明是哪个插件的哪个函数被调用了,为了使主程序在运行时能知道这个插件可以支持什么格式的音频又件插件程序还应输出一个函数供主程序查询用void GetSupportedformat(char* szFomrat)
至此,这个插件就制作完了,可以依样画葫芦再做一个PIug2.dll它’支持’.wma文件,下面来看主程序的实现。
3.1.2主程序的实现
主程序是一个基于对话框的标准Windows程序,它启动时会搜索约定目录(可以约定所有插件都存放在主程序所在目录的Plugins子目录下),并使用Wia32函数LoadLrbrary加载所有插件,每加载一个插件DLL,就调用另一个Win32函数GetProcAddress获取引出函数
GetSupportedformat的地址,并调用此函数返回插件所支持的格式名(即是音频文件的扩展名),然后把(格式名,DLL句柄)二元组保存下来,当用户通过菜单打开文件时,主程序会根据扩展名决定调用哪个插件的LoadFile函数,并指明此插件DLL的句柄为当前使用的插件的DLL句柄(比如保存到变量m_hlnst中),此后当用户通过按钮调用Play等其他功能时就调用句柄为m_hlnst的插件的相应功能,如:
typedef void (PLAY)();
if(m_hlnst)
{
PLAY=GetProcAddress(m_hlnst, "Play");
PLAY();
}
另外,当程序退出时,应该调用FreeLibrary函数卸载插件。
到此为止第一种实现插件系统的方法就介绍完了,可以看出,其实现的关键在于插件输出、函数的约定以及把插件所支持的格式名映射到插件DLL的句柄,后面将会看到,实际上每一种实现都是基于这种原理只不过是方式不同而已。
3.2第二种方法
第一种实现方法完全是结构化程序设计,存在接口不易维护等缺点,从而我们自然而然想到面向对象的解决方案——把API封装到类里。
我们定义抽象类如下
class IcppPlugin
{
public:
ICppPlugin ();
virtual void ICppPluginIcon()=0;
virtual void Release()=0;
virtual void GetSupportedFormat(char* szFormat)=0;
virtual void Load(constchar* szHeName)=0;
virtual void Play()=0;
virtual void Stop()=0;
virtual void Pause()=0;
};
其中Release成员函数将在后面介绍,其他成员函数意义与第一种实现中的同名函数相同,插件程序需要实现此抽象类,每个插件都有不同的实现而主程序仅通过接口(抽象类)来访问它们,现在来制作一个插件CppPlugin I.dll,它包含继承于ICppPlugin的类CppPlugin1。
class CppPlugin1 : public ICppPlugin //实现代码略
为使主程序能创建CppPlugin1对象,插件输出一个函数
bool CreateObject(void* pObj)
{
pObj=new CppPluginlo;
return *pObj!=NULL;
}
对象是在动态库中创建的,也应该在动态库中被销毁,这就是ICppplugin的成员函数Release的作用。当主程序使用完对象后,需要调用此函数来销毁对象。它的实现如下:
void CppPIugin1::Release() { delete this; //删除自己}
我们还可以再制作更多的插件,这些插件只需要给出ICppPlugin的不同实现,即改变类
Cppplugin1的成员函数实现即可,现在来看主程序的处理过程。
3.2.2主程序的实现
插件的加载过程与第一种方法相似,所不同的是加载DLL后,首先调用的是插件程序的CreateObject输出函数来创建对象:
typedef boot (*CreatoObject)(void *pObj);
//定义一个函数指针类型,获取Create06ject的地址,hInst为DLL的句柄
Create0bject CreatcObj = (CreateObject)GetProcAddtess(hInst, "Create0bject");
ICppPlugin* pObj = 0; //定义一个ICPPPlugjn的指针
CreateObj((void**)&pObj);//创建对象
接下来查询插件所支持的格式名,本方式中GetSupportedFormat已成为ICppPlugin的成员函数。
CString str;
pObj->GetSupportedFotmat(str,GetBuffer(8));
str.ReleaseBuffer;
另外,需要保存的除(格式名,DLL句柄)二元组映射外,还需保存(格式名,创建对象函数指针)二元组映射以备后用。
fstr存放的是格式名字符串的小写形式
m_formatMap [fstr」= hInst;
m_factoryMap[fstr] = CreatcObj;
同样在打开文件时选择使用哪个插件,m_pObj存放当前使用的对象的指针,定义如下:ICppplugin *m_p0bj=0;//在程序初始化时要把它置为0
if(m_pObj) {
m_pObj->Release();
m_pObj=0;
}
m_factoryMap [strExj((void")&m_pObj); //用CreatcObject
m_pObj->LoadFile((LPCSTR)strFileName); //strFileName是音频文件全路径名
以后就可以使用m_pObj来调用其他操作了,例如:
if(m_pObj) m_pObj->Play();
在主程序退出时需要卸载DLL,不必重复。
现在第二种实现插件系统的方式也介绍完了,这种方式基于C++的多态性,需要注意的是对象的创建和销毁方式,
3.3第三种方法
第二种实现方法其实已经是组件化程序的雏形了,可以胜任开发小型的插件系统,若要开发大中型的系统,则需要完全组件化的设计,COM (Component Object Model,组件对象模型)实际上就是一个实现插件的极好技术,基于COM建立的插件系统,主程序和各个插件可以用不同的编程语言写成(C++, VB, Delplu,Java等),COM能使它们无缝地结合在一起,篇幅所限,本文不详细介绍COM的原理与编程。
在这种实现方法中,插件是一个COM组件,确切地说,插件程序作为COM组件程序,包含了一个或多个COM对象。这些COM对象都实现了相同的COM接口,主程序通过这个COM接口来访问COM对象,即COM接口是主程序与插件通信的唯一手段几比如播放器插件所包含的COM对象都实现了如下COM接口(IDL定义):
interface ICppPlugin : IUnknown
前两种方式的插件一般放到程序的Plugin(自己设定)子目录下,但是因为COM组件对COM客户是位置透明的,所以主程序需要知道的已不是插件的具休位置和名字而是COM组件的CLSID或ProglD,可以选择把这些信息存放到指定的注册表子键下,也可以放到ini文件中等等,然而更好的方式是使用COM的组件类别(Component Category)索引机制,
COM允许实现者可以把相关的一组COM类组织到逻辑组(即组件类别)中,通常一个类别(category)中的所有COM类都实现同一组接口这些COM类共享同一个类别ID,称为CATID (category ID), CATID也是GUID它作为COM类的属牲被保存在注册表中COM类的"Implemented Categories’子键下在组件自注册时加入,每个类别在注朋表中都有它自己唯一的子键,由它的CATID命名,
另外,系统提供一个称为组件类别管理器(component category manager)的COM类,它实现了ICatRegister和ICatInfomation接口,分别用来注册和查询类别信息,
于是基于COM的插件系统就可以这样实现:
(1) 注册一个组件类别CATID_Plugin。
(2) 插件实现包含实现了ICppPlugin接口的COM类并注册为CATID_Plugin类别。
(3〕主程序在启动时使用组件类别管理器查询CATID_Plugin类别信息,得到此类别的所有COM类的CLSID,并创建相应的COM对象,获取其ICppPlugin接口,然后调用接口的GetSupportedFormat,方法得到该插件所支持的格式名,保存(格式名,ICppPlugin接口指针)映射。
(4) 程序在打开音频文件时根据扩展名决定使用哪个ICppPlugin接口指针调用LoadFile方法,并设置当前使用的接口指针m_pICppPlugin为该接口指针。
(5) 以后的操作(Play等)都使用m_pICppPlugin来调用直到打开不同类型的文件。
(6) 程序退出时释放掉COM对象并释放COM库所占用的资源。
详细代码这里不再给出。
至此三种创建插件系统的方式都介绍完毕,程序使用Visual C++6,0开发,在Windows 2000 Server上运行通过,
3.4 小结
上文所演示的例子中调用是单向的,即由插件暴露出接口,由主程序来调用,在实际应用中主程序也完全可以暴露出接口,由插件来调用从而使系统更加灵活,三种方法从结构化程序设计到面向对象的方法再到基于组件的软件开发,难度依次升高,功能逐渐强大,系统也越来越灵活,根据所要创建的插件系统的不同开发人员可以选择合适的实现方式,掌握技术原理是容易的,其实真正困难的是如何进行详细的应用分析,抽象出合适的接口,这样才能使整个插件系统拥有强大的可扩展性灵活性、健壮性和良好的可维护性。
HRESULT LoadFile(BSTR bstrFileName);
HRESULT GetSupportedFormat([out,retval) BSTR pbstrFormat);
HRESULT Play();
HRESULT Stop();
HRESULT Pause();
于是,插件的开发就是COM组件的开发,这里不再详述,唯一的问题是主程序如何知道哪些是它能使用的插件(就是COM组件)。前两种实现中,我们需要插件的具体位置和名字,所以约定插件都存放在主程序所在目录的。
结语
插件作为特殊的组件,具备组件的所有优秀的特性,这些特性使其在开发,推广,应用方面有重要的现实意义,基于插件技术的软件开发可以使产品专业化标准化系列化,通过不同规格和系列的插件的组合,可以快速地完成应用系统原型而通过对插件的局部修改来满足客户的需求和升级。
参考书目
Windows高级编程指南(第三版) 清华大学出版社1999/6.
设计模式-可复用面向对象软件的基础 机械工业出版社2000/9.
COM本质论 中国电力出版社2001
changy
2009-07-13
打赏
举报
回复
1.进程外COM
2.进程间通信,如果远程可以通过TCP/IP
这两种方法都可以达到你的目的
做鸡真好吃
2009-07-12
打赏
举报
回复
mark~
wshcdr
2009-07-08
打赏
举报
回复
进程外组件
chuan014
2009-07-08
打赏
举报
回复
如果程序已经成型了只是一个EXE,还有什么办法吗
我想了一下这样
我开发一个COM 名子叫coma,包含一些接口,让第三方的程序加载coma,在COM实现中和我的EXE程序进行进程间通讯。
总感觉这样不好啊。
fengrx
2009-07-08
打赏
举报
回复
只要在开发时将程序分模块,分层,并分别以DLL(动态链接库、COM)实现,在界面层直接调用这些基础库实现。
其他人想访问时,和楼主一样直接调用底层的库。
lijingchuan20
2009-07-08
打赏
举报
回复
封装DLL文件,并将头文件一同提供给第三方
写不动代码的人
2009-07-08
打赏
举报
回复
接口一定要定好
codelast.com
2009-07-08
打赏
举报
回复
除了以上各位说的方法,如果楼主要以exe形式出现的话,还可以用以下方法:
通过XMLRPC调用实现,你的exe中实现一个XMLRPC服务器,别人的程序就可以以函数调用的形式来调用你定义好的函数了。XMLRPC的库很多,在C++里可以用xmlrpc++。
superdiablo
2009-07-08
打赏
举报
回复
1、把你的exe做成一个COM,也就是进程外组件,该COM是直接包含在你的exe内部的。
2、使用Windows进程间通讯机制比如消息、事件等。
chuan014
2009-07-08
打赏
举报
回复
wshcdr 大哥,能不能说的具体一点,一定另开贴送100分
tesseract-ocr-setup-3.01-1
tesseract-ocr-setup-3.01-1谷歌开源ocr技术
迅雷云加速
开放
平台
接口
说明文档
任务信息查询和更新的流程: 1.调用
接口
查询信息时,先从虚任务查询任务信息,第一次调用的时候肯定没有信息 2.将查询操作与参数push到命令队列;参数: [in] url, 任务URL,不能为空,包括空字符字符长度不能超过2084 [in] path, 任务路径,不能为空,包括空字符字符长度不能超过260 [in] fileName...MiniThunderPlatform.
exe
XLBugReport.
exe
download_engine.dll minizip.dll、mini_unzip.dll
python 华泰股票交易
接口
_TradeApi 自带资金管理的A股
程序
化交易
接口
该楼层疑似违规已被系统折叠隐藏此楼查看此楼简介使用TradeApi为A股
程序
化交易
接口
, 支持使用C++、Python、Java、C#等语言调用,同时支持策略资金管理,大大方便了仓位自动计算、方便管理多个策略各自持仓和盈亏统计 ,支持多个证券账户下单同时支持多个证券账户交易。TradeApi的目标是「提供更专业的A股
程序
化交易
接口
。」对量化交易和
程序
化交易来说一件非常重要而又麻烦的事,就是如何将...
将B/S
程序
打包成
exe
,C#对外提供http
接口
,CefSharp 修改浏览器默认白色背景
C#嵌套chrome,WCF提供http
接口
基于Visual C#的
接口
基础教程
第一节
接口
慨述
接口
(interface)用来定义一种
程序
的协定。实现
接口
的类或者结构要与
接口
的定义严格一致。有了这个协定,就可以抛开编程语言的限制(理论上)。
接口
可以从多个基
接口
继承,而类或结构可以实现多个
接口
。
接口
可以包含方法、属性、事件和索引器。
接口
本身不提供它所定义的成员的实现。
接口
只指定实现该
接口
的类或
接口
必须提供的成员。
接口
好比一种模版,这种模版定义了对象必须实现的方
ATL
3,248
社区成员
48,529
社区内容
发帖
与我相关
我的任务
ATL
ATL,Active Template Library活动(动态)模板库,是一种微软程序库,支持利用C++语言编写ASP代码以及其它ActiveX程序。
复制链接
扫一扫
分享
社区描述
ATL,Active Template Library活动(动态)模板库,是一种微软程序库,支持利用C++语言编写ASP代码以及其它ActiveX程序。
社区管理员
加入社区
获取链接或二维码
近7日
近30日
至今
加载中
查看更多榜单
社区公告
暂无公告
试试用AI创作助手写篇文章吧
+ 用AI写文章