科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道Lotus C API Extension Manager 应用举例

Lotus C API Extension Manager 应用举例

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本文对 Lotus C API 中的 Extesnsion Manager 进行了探讨,并用一个应用工程实例对 Extension Manager 的使用和设计过程进行了详细的说明。阅读本文,需要具有基本的 Lotus C API 编程经验以及多线程和 Socket 编程概念。

作者:www.ibm.com 来源:www.ibm.com 2007年9月14日

关键字: 技巧 应用 IBM lotus Office

  • 评论
  • 分享微博
  • 分享邮件

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 <stdlib.h>
……
/*Notes Domino Header File*/
#include <global.h>
……
/*===== 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 的专题介绍。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章