|
XPCOM LINUX下的组件开发技术 COM技术作为微软推行的一种组件技术,在WINDOWS平台站有重要地位,在模块重用,跨语言通信等方面都能见到其身影。但今天给我要介绍的是LINUX下的COM实现----XPCOM,这是MOZILLA浏览器项目中所使用的基本技术,我们可以用C++制作XPCOM组件,在C++客户程序或MOZILLA浏览器中通过JAVASCRIPT脚本来调用组件,从而实现软件模块的复用。 1、 配置XPCOM的开发环境。 首先到MOZILLA的FTP下载Gecko-sdk包,这是XPCOM的开发包,MOZILLA的源码中也包括该SDK。解压该tgz包,可以看到生成大约十多个目录: /sdk/gecko-sdk/ /sdk/gecko-sdk/xpcom/bin /sdk/gecko-sdk/xpcom/idl /sdk/gecko-sdk/xpcom/include /sdk/gecko-sdk/nspr ...... 这里说明一下其中的一些基本部分。 /sdk/gecko-sdk/xpcom/bin下主要包含了一些文件: xpidl:这是idl编译器,用以根据idl产生c++头文件或组件类型库文件. Regxpcom:这是组件注册工作,如果我们在MOZILLA浏览器中调用组件,其实不会用该工具。 Xpt-dump:类型库查看程序,用来查看.xpt文件中的组件信息。 libxpcomglue.a:这是XPCOM的基本库文件,在生成组件时将会被连接到我们的组件库中。 /sdk/gecko-sdk/xpcom/idl,该目录中包含了idl数据类型定义文件。 /sdk/gecko-sdk/xpcom/include,其中包含了制作XPCOM时所需要的基本的C++头文件。 /sdk/gecko-sdk中还包含了其它一引起目录,如/sdk/sdk/gecko-sdk/string/include,其中包含了XPCOM中常字符串类的C++头 文件,如果我们的组件中需要使用这些类,只需包含进必要的头文件及库文件即可。 2、 撰写idl文件。 这里要先用到一个uuidgen(LINUX下类似MS GUIDGEN的一个命令行程序)用以产生组件的uuid, 我们将其输出先重定向到一个文本中,呆会儿即可使用,这里我们举一个简单的例子,来演示组件的生成过程。 Idl文件如下: //filename: nsIMyCom.idl //begin idl -------------------------------------- #include "nsISupports.idl" [scriptable, uuid(5217115e-11fe-4d01-966d-9b27ffda6498)] interface nsIMyCom:nsISupports { void Hello(in string in_str, [retval] out string out_str); }; //end idl----------------------------------------- 好了,该组件很简单,只有一个接口,并且也只有一个方法,该方法有一个字符串输入参数in_str,并且有一个字符串返回值out_str。 3、编译该idl文件,并完成该组件对应的C++实现。 /sdk/gecko-sdk/xpcom/bin/xpidl -I/sdk/gecko-sdk/xpcom/bin/idl -m header nsIMyCom.idl 如果没有错误,这时在当前目录下将会生成一个nsIMyCom.h文件,该文件是idl编译器对应上面的idl文件所生成的c++头文件。 下面是编译器生成的nsIMyCom.h文件内容: //-------------------------------------------------------- #ifndef __gen_nsIMyCom_h__ #define __gen_nsIMyCom_h__ #ifndef __gen_nsISupports_h__ #include "nsISupports.h" #endif /* For IDL files that don''''t want to include root IDL files. */ #ifndef NS_NO_VTABLE #define NS_NO_VTABLE #endif /* starting interface: nsIMyCom */ #define NS_IMYCOM_IID_STR "5217115e-22fe-4d01-966d-9b27ffda6498" #define NS_IMYCOM_IID / {0x5217115e, 0x22fe, 0x4d01, / { 0x96, 0x6d, 0x9b, 0x27, 0xff, 0xda, 0x64, 0x98 }} class NS_NO_VTABLE nsIMyCom : public nsISupports { public: NS_DEFINE_STATIC_IID_ACCESSOR(NS_IMYCOM_IID) /* void Hello (in string in_str, [retval] out string out_str); */ NS_IMETHOD Hello(const char *in_str, char **out_str) = 0; }; /* Use this macro when declaring classes that implement this interface. */ #define NS_DECL_NSIMYCOM / NS_IMETHOD Hello(const char *in_str, char **out_str); /* Use this macro to declare functions that forward the behavior of this interface to another object. */ #define NS_FORWARD_NSIMYCOM(_to) / NS_IMETHOD Hello(const char *in_str, char **out_str) { return _to Hello(in_str, out_str); } /* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */ #define NS_FORWARD_SAFE_NSIMYCOM(_to) / NS_IMETHOD Hello(const char *in_str, char **out_str) { return !_to ? NS_ERROR_NULL_POINTER : _to->Hello (in_str, out_str); } #if 0 /* Use the code below as a template for the implementation class for this interface. */ /* Header file */ class nsMyCom : public nsIMyCom { public: NS_DECL_ISUPPORTS NS_DECL_NSIMYCOM nsMyCom(); virtual ~nsMyCom(); /* additional members */ }; /* Implementation file */ NS_IMPL_ISUPPORTS1(nsMyCom, nsIMyCom) nsMyCom::nsMyCom() { /* member initializers and constructor code */ } nsMyCom::~nsMyCom() { /* destructor code */ } /* void Hello (in string in_str, [retval] out string out_str); */ NS_IMETHODIMP nsMyCom::Hello(const char *in_str, char **out_str) { return NS_ERROR_NOT_IMPLEMENTED; } /* End of implementation class template. */ #endif #endif /* __gen_nsIMyCom_h__ */ //--------------------------------------------------------- 从上面可以看到, xpidl生成了对应该接口的头文件,同时还包括对该头文件实现的C++类模板.下一步的工作一样很轻松, 我们将#if 0 至#endif 之间的代码分别复制到新建的nsMyCom.h 和nsMyCom.cpp文件中, 注意其中有新增的代码,下面是生成的两个文件. //filename: nsMyCom.h #include "nsImyCom.h" #define NS_MYCOM_CID / {0x5217115e, 0x22fe, 0x4d01, { 0x96, 0x6d, 0x9b, 0x27, 0xff, 0xda, 0x64, 0x98 }} //类似WINDOWS 中CLSID #define NS_MYCOM_CONTRACTID "@westsoft.org/mycom;1" //类似WINDOWS中的progid; class nsMyCom : public nsIMyCom { public: NS_DECL_ISUPPORTS NS_DECL_NSIMYCOM nsMyCom(); virtual ~nsMyCom(); /* additional members */ }; //filename: nsMyCom.cpp #include "nsMyCom.h" #include "nsMemory.h" #include <cstdio> #include <cstdlib> #include <string> NS_IMPL_ISUPPORTS1_CI(nsMyCom, nsIMyCom) //此处的宏已修改. nsMyCom::nsMyCom() { } nsMyCom::~nsMyCom() { } /* void Hello (in string in_str, [retval] out string out_str); */ NS_IMETHODIMP nsMyCom::Hello(const char *in_str, char **out_str) { printf("\n-----------------\n"); printf("%s\n", in_str); std::string str_tmp = "your input is: "; str_tmp += in_str; *out_str = (char*)malloc(str_tmp.length() + 1); *out_str = (char*)str_tmp.c_str(); return NS_OK; } 4、完成组件的工厂方法及注册模块。 组件本身的实现就上面两个类即可以了. 但是我们仅把上面的类生成动态库是不能作为组件工作的,我们还需要做一件事情.实现组件的注册及创建相关的功能.这几乎是一个固定的模式. 下面是该部分的代码,跟MS中的实现类似,用了许多的宏: #include "nsIGenericFactory.h" #include "nsMyCom.h" NS_GENERIC_FACTORY_CONSTRUCTOR(nsMyCom) static NS_METHOD nsMyComRegistrationProc(nsIComponentManager *aCompMgr, nsIFile *aPath, const char *registryLocation, const char *componentType, const nsModuleComponentInfo *info) { return NS_OK; } static NS_METHOD nsMyComUnregistrationProc(nsIComponentManager *aCompMgr, nsIFile *aPath, const char *registryLocation, const nsModuleComponentInfo *info) { return NS_OK; } NS_DECL_CLASSINFO(nsMyCom) static const nsModuleComponentInfo components[] ={ { "nsMyCom Component", NS_MYCOM_CID, NS_MYCOM_CONTRACTID,nsMyComConstructor, nsMyComRegistrationProc /* NULL if you dont need one */, nsMyComUnregistrationProc /* NULL if you dont need one */, NULL /* no factory destructor */, NS_CI_INTERFACE_GETTER_NAME(nsMyCom), NULL /* no language helper */, &NS_CLASSINFO_NAME(nsMyCom) } }; NS_IMPL_NSGETMODULE(nsMyComModule, components) 5、制作Makefile,生成,安装组件 好了,我们可以编写Makefile文件,来编译我们刚才编写的组件了. #filename:Makefile #begine------------------------------------- CPP = g++ CPPFLAGS += -fno-rtti -fno-exceptions -shared GECKO_SDK_PATH = /sdk/gecko-sdk XPIDL = $(GECKO_SDK_PATH)/xpcom/bin/xpidl CPPHEADER = -m header TYPELIB = -m typelib REGDIR = /usr/local/lib/mozilla-1.6 OUTDIR = $(REGDIR)/components GECKO_CONFIG_INCLUDE = -include mozilla-config.h GECKO_DEFINES = -DXPCOM_GLUE GECKO_INCLUDES = -I$(GECKO_SDK_PATH) \ -I$(GECKO_SDK_PATH)/xpcom/include\ -I$(GECKO_SDK_PATH)/nspr/include GECKO_LDFLAGS = -L$(GECKO_SDK_PATH)/xpcom/bin -lxpcomglue \ -L$(GECKO_SDK_PATH)/nspr/bin -lnspr4 GECKO_IDL = -I$(GECKO_SDK_PATH)/xpcom/idl build: idl nsMyCom.o nsMyComModule.o $(CPP) $(CPPFLAGS) -o libxpmycom.so $(GECKO_DEFINES) \ $(GECKO_LDFLAGS) nsMyCom.o nsMyComModule.o chmod +x libxpmycom.so idl: nsIMyCom.idl $(XPIDL) $(GECKO_IDL) $(CPPHEADER) nsIMyCom.idl $(XPIDL) $(GECKO_IDL) $(TYPELIB) nsIMyCom.idl nsMyCom.o: nsMyCom.cpp $(CPP) $(GECKO_CONFIG_INCLUDE) $(GECKO_DEFINES) \ $(GECKO_INCLUDES) -c nsMyCom.cpp -o nsMyCom.o nsMyComModule.o: nsMyComModule.cpp $(CPP) $(GECKO_CONFIG_INCLUDE) $(GECKO_DEFINES) \ $(GECKO_INCLUDES) -c nsMyComModule.cpp -o nsMyComModule.o install: cp nsIMyCom.xpt $(OUTDIR)/ cp libxpmycom.so $(OUTDIR)/ clean: rm *.o rm *.so rm *.*~ rm *~ #end------------- 如果一切无误,我们make之后,g++就会在当前目录下生成libxpmycom.so库文件。然后再将该组件安装到mozilla的组件目录中: make install 该组件库及对应的类型库nsIMyCom.xpt将会被拷到/usr/local/lib/mozilla-1.6/components(要确认你的组件目录,如mozilla1.4目录一般为usr/local/lib/mozilla-1.4/components)目录中。 这时我们可以从控制台启动mozilla浏览器,在浏览器输出的一系列信息中,将会有该组件被注册成功的信息。 6、在html/javascript中测试该组件。 该html如下: //------------------------------------ <html> <head> <title> 测试XPCOM组件 </title> </head> <body> <script> netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); var mycom = Components.classes["@westsoft.org/mycom;1"].createInstance(); mycom = mycom.QueryInterface(Components.interfaces.nsIMyCom); function testxpcom(f) { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); var ret_string; ret_string = mycom.Hello(document.form_test.input_string.value); alert(ret_string); } </script> <form name="form_test"> 输入信息: <textarea name = "input_string" cols = "70" rows = "5"></textarea> <input type="button" value="testxpcom" onClick = "testxpcom(this.form);"> </form> </body> </html> //-------------------------------------------------- 我们在mozilla中打开该html,在输入框中输入一些文字,在点击testxpcom后,怎么样,看到从组件返回的信息了吗? 另外,上面的输入信息如果是中文,则返回中会产生乱码,xpcom idl中有nsAString, wstring等支持unicode的字符串类型,由于在参数的传递中会涉及一些转换,因而未提及。 以上只是展示了XPCOM组件的基本技术,还有许多内容未提及,如果要将该技术应用于项目中,更多的资料请查阅mozilla的网上资源。
|