firefox的XPCOM的COM編程

XPCOM
LINUX下的組件開發技術
 
COM技術作爲微軟推行的一種組件技術,在WINDOWS平臺站有重要地位,在模塊重用,跨語言通信等方面都能見到其身影。但今天給我要介紹的是LINUX下的COM實現----XPCOM,這是MOZILLA瀏覽器項目中所使用的基本技術,我們可以用C++製作XPCOM組件,在C++客戶程序或MOZILLA瀏覽器中通過JAVASCRIPT腳本來調用組件,從而實現軟件模塊的複用。
 
1、         配置XPCOM的開發環境。
首先到MOZILLAFTP下載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的網上資源。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章