Lotus C API Extension Manager 應用舉例

genusBIT發表於2008-09-11

本文對 Lotus C API 中的 Extesnsion Manager 進行了探討,並用一個應用工程例項對 Extension Manager 的使用和設計過程進行了詳細的說明。閱讀本文,需要具有基本的 Lotus C API 程式設計經驗以及多執行緒和 Socket 程式設計概念。

Extension Manager基本概念

什麼是 Extension Manager

Extension Manager 是 Lotus C API 提供的一個功能非常強大的設計機制,它允許應用程式設計者向 Notes/Domino 系統註冊自己感興趣的事件,例如 EM_NSFDBCLOSESESSION、EM_NSFDBCLOSE 等。從而在 Notes/Domino 進行核心操作之前(或者之後,取決於在事件註冊中使用的是 EM_REG_BEFORE 還是 EM_REG_AFTER)來執行自己設計的特製外掛程式。

Extension Manager程式設計規範

Extension Manager 外掛程式的設計框架要符合一定的規則,這樣才能被 Notes/Domino 系統在合適的時機呼叫。關於Extension Manager 的詳細設計方法,請參考隨 Lotus C API 一起釋出的設計文件,基本上來講,一個具備基本功能的 Extension Manager 外掛程式應該具備以下幾個要素:

  • DLL 入口函式:
    Extension Manager 外掛程式必須被編譯為可執行的程式庫(例如,Windows 系統的動態連結庫 dll 或者是 UNIX 系統的 shared object)。程式庫的結構和命名規則是跟平臺相關的,詳細資訊請參考 Lotus C API 使用者手冊第十二章第二節:"Platform-Specific Naming Conventions" 。在 DLL 入口函式中,應當完成外掛程式例項的建立和釋放,並且負責在外掛程式退出之前,登出向 Notes/Domino 系統註冊的 EM_XX 事件。
  • 外掛程式入口函式
    該函式是定製外掛程式的入口函式,EM事件的註冊過程將會在此函式中加以實現。該函式宣告格式如下所示
    STATUS LNPUBLIC MainEntryPoint (void);
    該入口函式的名字可任意給定,但是必須在模組定義檔案(.def檔案)中將其宣告為匯出函式(EXPORTS function),並且匯出序號為1。
    例子:
    LIBRARY nextmngr INITINSTANCE
    EXPORTS
    	MainEntryPoint		@1
    


    在註冊回掉函式之前,推薦使用 EMCreateRecursionID() 函式,這樣可以防止一個外掛程式被多次呼叫。
  • 外掛程式回撥函式
    該函式是 Extension Manager 外掛程式的業務處理函式,負責在收到註冊事件通知後進行定製處理。

Extension Manager 程式執行條件

要想執行一個定製的 Extension Manager 程式,需要做兩件事情:

  • 將編譯成功的 DLL 檔案放在 Notes/Domino 的主目錄下
  • 修改 notes.ini 檔案,增加一個變數如下所示
    EXTMGR_ADDINS=NEXTMNGR

如果有多個外掛程式,在外掛程式名之間用逗號格開。如果多個外掛程式註冊了同一個事件,則按照 notes.ini 檔案中的外掛程式的註冊順序來依次進行處理。

在瞭解了以上基礎知識之後,我們將以 Windows 平臺為例,給出一個簡單而典型的 Extension Manager程式結構,本示例程式只是用來說明 Extension Manager 的程式架構和處理邏輯,不能編譯執行,具體的 Extension Manager 示例程式,請參考隨 Lotus C API 一起釋出的 Sample。


Extension Manager程式結構示例
/* Extension Manager程式結構示例*/
/*system header file*/
#include 
……
/*Notes Domino Header File*/
#include 
……
/*===== GLOBAL VARIABLES =============================*/
HEMREGISTRATION	  hHandler; //外掛程式上下文控制程式碼
EMHANDLER   gHandlerProc;   //外掛程式回撥函式控制程式碼
WORD        gRecursionID;    //防止該程式被多次呼叫
CRITICAL_SECTION    gCriticalSection; // 用於多執行緒同步
/*===== LOCAL FUNCTION PROTOTYES ======================================*/
STATUS  LNPUBLIC  MainEntryPoint( void ); 
//外掛程式入口函式
STATUS  LNPUBLIC  EMHandlerProc( EMRECORD FAR * pExRecord);     
// 外掛程式回撥函式
BOOL  WINAPI  DllMain( HINSTANCE hInstance, DWORD fdwReason, 
LPVOID lpReserved );  //  DLL入口函式
/*==========================================================================*/
STATUS  LNPUBLIC  MainEntryPoint( void )
{
    STATUS    error= NOERROR;
error = EMCreateRecursionID( &gRecursionID );	
error = EMRegister(EM_GETPASSWORD, EM_REG_BEFORE | EM_REG_AFTER,
   (EMHANDLER)gHandlerProc,  gRecursionID,  &hHandler);
    return( error );
}
/*==========================================================================*/
STATUS LNPUBLIC EMHandlerProc( EMRECORD FAR * pExRecord )
{
    STATUS error = 0;
switch(pExRecord->EId) {
	case  EM_GETPASSWORD:
		{
	          return(ERR_BSAFE_USER_ABORT );
}
return  error;
}
/*==========================================================================*/
BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD fdwReason, LPVOID lpReserved )
{
STATUS error=NOERROR;
	switch(fdwReason)
	{
		case DLL_PROCESS_ATTACH:
			InitializeCriticalSection(&gCriticalSection);
			gHandlerProc = (EMHANDLER)MakeProcInstance((FARPROC)EMHandlerProc,
			hInstance);
			break;
		case DLL_PROCESS_DETACH:
			error = EMDeregister(hHandler);
			FreeProcInstance( gHandlerProc );
			DeleteCriticalSection(&gCriticalSection);			
			break;
	}
	return( TRUE );
	UNREFERENCED_PARAMETER(lpReserved);
}

Extension Manager 工程例項

接下來,本文將介紹 Extension Manager 的一個工程例項,並希冀由這個工程例項對 Lotus C API 應用程式開發者在使用 Extension Manager 實現定製應用程式時給予一定的啟發和指導。

1) 工程背景:

某客戶為了加強內部辦公環境的管理,需要強制使用者加入 Windows 特定域。客戶希望採取繫結郵件系統訪問的方式:即限制使用者只有登入 Windows 域後才可以訪問郵件系統。

2) 工程設計:

為實現客戶需求,我們可以設想在客戶端部署一個 EM 程式。該客戶端 EM 程式註冊 EM_GETPASSWORD 事件,在Notes彈出密碼輸入對話方塊之前判斷當前 Windows 使用者是否在域中,如果不在域中就返回 ERR_BSAFE_USER_ABORT,這樣可以阻止 Notes 客戶端連線 Domino Server。程式片斷請參考 Extension Manager 程式結構示例。

一切進行的很順利,似乎工作到此已經萬事大吉了。然而且慢,如果仔細考慮一下,這種方案是不可取的,如果客戶端不安裝這個EM程式怎麼辦?或者如果使用者解除安裝這個 EM 程式的話怎麼辦?這樣的話客戶端完全可以繞開認證過程。

所以我們必須考慮在伺服器端進行統一的控制,這就需要在伺服器端也部署一個 EM 程式。該伺服器 EM 程式註冊 EM_SECAUTHENTICATION 事件,對每位向郵件伺服器傳送訪問請求的使用者進行實時授權鑑別,只有在客戶端登入 Windows 域的情況下,伺服器端 EM 程式才開啟該使用者訪問郵件伺服器的限制。

現在的問題是伺服器端 EM 程式如何鑑別來訪客戶端是否在 Windows 域中?答案是 Socket 通訊方法:客戶端 EM 程式在經過相關判斷之後,把 Notes 使用者的資訊寫入到安裝 Domino Server 的伺服器的某個檔案(如Info.DAT)中,伺服器端 EM 程式在認證來訪 Notes 使用者的時候查詢該檔案,如果發現來訪 Notes 使用者資訊在 Info.DAT 檔案中存在,則授權該使用者訪問郵件伺服器,否則則拒絕其訪問。

系統的整體架構如下圖所示:


圖一 系統整體架構
圖一 系統整體架構

3) 模組設計與實現

系統分為三個元件,分別是:

  • Notes 客戶端 EM 元件

    功能說明:負責判斷當前 Notes 使用者的 Windows 域資訊,並把相關資訊傳送給通訊伺服器,在接收到通訊伺服器的確認資訊之後,更新登陸時間窗資訊。(時間窗是一個時間段,某 Notes 客戶端在域中登陸 Domino Server 之後,允許在時間窗內該 Notes 客戶端不登陸域也能訪問郵件伺服器)

    演算法如下:

    I. 捕獲 EM_GETPASSWORD 訊息

    II. 讀取登錄檔的內容,取得當前時間跟登錄檔中上一次登記的時間做比較,判斷時間窗是否過期。

    III. 如果時間窗未過期,即該 Notes 客戶端已經在指定時間段內向 Server 註冊過了,則直接返回 ERR_EM_CONTINUE. 把控制權交給 Notes,允許其連線郵件伺服器。

    IV. 如果時間窗過期,判斷當前 Windows 使用者否在域中,如果在域中,從登錄檔中讀取通訊伺服器的 IP 和埠,把 Notes User 的資訊傳送過去,然後等待 Server 的響應。如果收到 Server 的響應,那麼更新登錄檔中日期表項

    V. 如果當前 Windows 使用者不在域中,說明在時間窗之內沒有人在域中登陸過 Domino Server,返回 ERR_BSAFE_USER_ABORT 阻止 Notes 連線郵件伺服器。

  • Domino 伺服器端 EM 元件

    功能說明:捕獲當前試圖連線自己的 Notes 使用者,讀取使用者資訊檔案,判斷該當前使用者是否為合法使用者,從而決定是否授權該使用者訪問郵件伺服器。

    演算法如下:

    I. 註冊 EM_SECAUTHENTION 事件

    II. 在事件發生時獲取當前申請連線的使用者名稱字

    III. 查詢使用者資訊檔案,若有跟當前使用者匹配的資訊,說明該使用者是合法使用者,返回 ERR_EM_CONTINUE 將控制權交給 Domino Server。否則,返回 ERR_SECURE_FAILED_AUTH,拒絕該使用者的連線請求。

  • 通訊伺服器程式

    功能說明:負責讀取所有客戶端傳送給自己的使用者名稱資訊,儲存在使用者資訊檔案中。以備 Domino Server 側的 EM 程式訪問。

    詳細設計以及說明,請參見通訊伺服器架構圖以及其說明。

    I. 通訊伺服器架構圖



    圖二 通訊伺服器架構圖
    圖二 通訊伺服器架構圖

    II. 說明:

    該通訊伺服器是一個多執行緒併發程式,具備了四個模組,分別是:

    a) 處理伺服器模組
    功能說明:負責整個通訊伺服器程式的啟動和中止,並記錄整個系統的 Log 資訊

    b) 訊息件模組
    功能說明:該模組由處理伺服器模組啟動,在啟動的時候啟動了三個執行緒,分別是:

    • 接收訊息執行緒
      該執行緒啟動通訊模組,並等待通訊模組的訊息,如果通訊模組通知該執行緒有訊息到達,那麼去獲得該訊息並寫入接收訊息佇列裡面
    • 處理訊息執行緒
      該執行緒實時監測接收訊息佇列裡面是否有資料,如果有資料,那麼取出該資料,並寫入使用者資訊檔案裡面,寫入成功後,傳送一個 UDP 訊息到傳送訊息佇列裡面,UDP 訊息包括某個客戶端的 IP,埠以及確認訊息。其定義為:
      struct UDPDATA{
      	unsigned long	ulIPAddr;
      	unsigned short  wPort;
      	string	sData;
      };
      

    • 傳送訊息執行緒
      該執行緒實時監測傳送訊息佇列裡面是否有資料,如果有資料那麼取出來進行分析,獲得 IP 和埠,然後通過通訊模組把資料傳送出去。

    c) 通訊模組
    功能說明: 該模組封裝了 Socket 通訊的 API,完成網路通訊功能。

    d) 訊息佇列模組
    功能說明: 該模組實現了 Message Queue 的功能,是一個雙向連結串列。目的是為了資料的快取,防止使用者資料丟失的可能性。系統有兩個訊息佇列,一個是接收訊息佇列,負責儲存接收的訊息;另外一個是傳送訊息佇列,負責儲存傳送的訊息。

總結

Lotus C API 是一個強大的 Notes/Domino 二次開發工具,而 Extension Manager 更是其中極為重要而又極為複雜的一部分,通過這個工程例項,讀者可以對此有一定的認識。今後,我們將繼續為讀者進行有關 Extension Manager 的專題介紹。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/14751907/viewspace-444280/,如需轉載,請註明出處,否則將追究法律責任。

相關文章