Microsoft Agent技术应用 --AgentShell的实现原理介绍
[摘要] 本文介绍了如何应用Agent的以及AgentShell的实现原理和几个重要的技术处理。
[关键词] Agent,COM,角色,语音识别,语音合成。
对Agent编程的方法主要有使用VB,VC等语言进行ActiveX调用,除此之外还有直接通过VC进行COM 编程调用。在VB中调用Agent是最简单不过了,但由于VB程序本身存在诸多缺陷,很难在实际中应用。 而在VC中,由于Agent内部完全采用了UNICODE编码,同时还要处理各种繁杂的COM接口,从而也存在一 定的问题。AgentShell是建立在Agent和应用程序之间的一个外壳程序,通过它可将Agent复杂的COM接 口封装起来,转变为简单的函数调用,很好的实现对Agent的控制。同时AgentShell也作为一个独立的程 序,可处理英文自动朗读等功能,本文将详细介绍其实现原理。 (一) 原理介绍 AgentShell和Agent Server的连接是通过COM调用来实现的,对于与应用程序的通信是通过WM_COPYDATA 消息来实现的, 下图表示出AgentShell与其它程序的关系: [ Agent Server ] | [ COM调用 ] | [ AgentShell ] | [ 消息 ] | [ 应用程序 ]
将一个Agent控制加载相应的动画和语音码我们称之为“角色”,一般使用COM调用创建一个Agent角色, 要经过以下几个过程: [ 初始化COM ] | [ 连接Agent COM Sever,创建Agent控制 ] | [ 注册Agent控制的消息反应器(Notify Sink) ] | [ 加载角色数据文件,创建一个角色(Character) ] | [ 设置角色的语言、初始位置以及其它属性 ] | [ 显示角色 ]
AgentShell中定义以下全局变量来控制角色的属性和动作: 角色的消息ID: long g_lNotifySinkID。 角色ID: long g_lMyAgentID。 Agent控制指针: IAgentEx *g_pAgentEx。 角色指针: IAgentCharacterEx *g_pMyAgent。 角色消息反应器指针: AgentNotifySink *g_pSink。
使用以上变量可很容易的调用Agent的功能,如显示角色: BOOL agentShow() { HRESULT hRes; long lRequestID;
if( !g_pMyAgent) return FALSE; hRes = g_pMyAgent->Show(FALSE, &lRequestID); if (FAILED(hRes)) return FALSE; return TRUE; }
(二) 角色的语言处理 目前Agent支持很多种语言,不仅是显示,还有语音合成和语音识辨(对于中文,目前仅支持显示)。 语言又分为主语言和子语言(或为副语言),如中文的主语言为中文(LANG_CHINESE),子语言则可为 简体(SUBLANG_CHINESE_SIMPLIFIED)和繁体等。AgentShell中定义两个全局变量表达角色的语种:
主语言:DWORD g_nMainLang。 子语言:DWORD g_nSubLang。 这样程序内必须根据当前语言的不同来显示不同的信息,如程序退出时的问候信: 首先定义不同的语言信息,可以为宏定义或资源数据: #define MES_GOODBYEL"Goodbye!" #define MES_GOODBYE_CH L"再见!" #define MES_GOODNIGHTL"Good night!" #define MES_GOODNIGHT_CH L"祝您晚安!" 以下为实现退出提示代码: void Goodbye() { if( g_bAgentOK) { SYSTEMTIME time; agentStop(); agentShow(); agentPlay(L"Wave"); GetLocalTime(&time); // 根据时间不同提示不同信息 if( g_nMainLang == LANG_ENGLISH) { // 提示英文信息 if( time.wHour < 19) agentSpeak(MES_GOODBYE); else agentSpeak(MES_GOODNIGHT); } else { // 提示中文信息 if( time.wHour < 19) agentSpeak(MES_GOODBYE_CH); else agentSpeak(MES_GOODNIGHT_CH); } agentHide(); // 等待若干时间 Sleep(MAX_QUIT_TIME); } } 当然以上介绍的只是一种较为简单的方法,仅在于描述这种原理。
(三) 实现自动朗读英文 实现自动朗读实际上是响应剪贴板消息的过程,当复制选种的文本信息时,系统自动发送WM_DRAWCLIPBOARD 消息给所有剪贴板监视队列中的窗口,相应的窗口只要读取当前剪贴板内的信息进行朗读即可,具体实现如下:
安装剪贴板监视: void InstallClipSpy() { g_hNextWnd = SetClipboardViewer(g_hMainWnd); } 主窗口的回调函数中相应剪贴板消息: LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { // 剪贴板窗口队列发生变化 case WM_CHANGECBCHAIN: hwndRemove = (HWND)wParam; // handle of window being removed hwndNext = (HWND) lParam; if( hwndRemove == g_hNextWnd) { g_hNextWnd = hwndNext; } if( g_hNextWnd) { SendMessage(hwndNext, WM_CHANGECBCHAIN, wParam, lParam); } // 剪贴数据发生变化 case WM_DRAWCLIPBOARD: // 是否自动阅读 if( g_bEnableRead) { // 阅读剪贴板信息 ReadClipText(); } if( g_hNextWnd) { SendMessage(g_hNextWnd,WM_DRAWCL |