XPCOM–LINUX下的組件開發技術的一些補充與說明

來源:互聯網
上載者:User

XPCOM--LINUX下的組件開發技術的一些補充與說明
   
原文出自:《世界商業評論》ICXO.COM ( 日期:2004-07-14 13:56)
--------------------------------------------------------------------------------
   boise  bjgxjob@163.com
--------------------------------------------------------------------------------
   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//在這裡需要注意的是:nsIMyCom以"nsI"為首碼,
//可以方便在之後的C++檔案中產生相應的類名,類名為nsI之後的部份,如MyCom;
//若介面名前三個字母為其它的字元,一般產生的C++類的類名為_MYCLASS_
{
      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 dont 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) //此處的宏已修改:注意這裡與自動產生的C++代碼的不同之處:NS_IMPL_ISUPPORTS1

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(%sn, 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、完成組件的Factory 方法及註冊模組。
組件本身的實現就上面兩個類即可以了. 但是我們僅把上面的類產生動態庫是不能作為組件工作的,我們還需要做一件事情.實現組件的註冊及建立相關的功能.這幾乎是一個固定的模式.

下面是該部分的代碼,跟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,//這裡,"Constructor"是固定的,nsMyCom是類名
    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)//nsMyComModule是模組源碼檔案編譯後的模組名:nsMyComModule

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
#產生C++標頭檔
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庫檔案,nsIMyCom.xpt是相應的類型檔案。然後再將該組件安裝到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內部已經正常地取得了GB2312的漢字編碼的資料,要在測試頁面上由JavaScript來正常地解釋與顯示這些編碼,就必須要把GB2312轉換成為UNICODE編碼。在此之前,需要修改前面所定義的IDL檔案:nsIMyCom.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 wstring out_str);
};
//end idl-----------------------------------------
產生相應的HEADER檔案後,修改C++類檔案並編譯,安裝後運行就可以在Mozilla中正常顯示出漢字了。
GB2312轉換為UNICODE的代碼如下:
//begin-------------------------
#include <iconv.h>
#define MAX_PATH 400
char * GetUnicode(char * ucInGB2312, char *ucOutUnicode)
{
 if(ucInGB2312 == NULL || ucOutUnicode == NULL)
 {
  return "/0";
 }
 char *strIn = ucInGB2312;
 char *strRet = ucOutUnicode;
 char tmp[MAX_PATH*10] = {0};
 char *pTmp = tmp;
 size_t nInLen = strlen(ucInGB2312);
 size_t nOutLenLeft = nInLen * 3;
 size_t nOrgLen = nInLen*3;
 
 iconv_t cd = 0;
 if((cd = iconv_open("UNICODE", "GB2312")) == (iconv_t)-1)
 {
  return "/0";
 }
 if(iconv(cd, &strIn, &nInLen, &pTmp, &nOutLenLeft) == (size_t)-1)
 {
  return "/0";
 }
 memcpy(ucOutUnicode, tmp, (nOrgLen - nOutLenLeft));
 iconv_close(cd);
 return strRet;
}

//end--------------------------

以上只是展示了XPCOM組件的基本技術,還有許多內容未提及,如果要將該技術應用於項目中,更多的資料請查閱mozilla的網上資源。

 以上是根據《XPCOM--LINUX下的組件開發技術》做了補充說明,希望對這方面的開發人員有協助,不到之處請多多指教!
若有問題與我聯絡:bjgxjob@163.com

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.