分享
 
 
 

Microsoft Agent技术应用

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

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_DRAWCLIPBOARD,wParam, lParam);

}

获取剪贴板信息并且朗读:

void ReadClipText()

{

if( g_bAgentOK)

{

// 只有文本文件才朗读

if( IsClipboardFormatAvailable(CF_TEXT))

{

if (OpenClipboard(g_hMainWnd))

{

LPWSTR pwsz;

UINT cch;

HGLOBAL hglb;

LPSTR lpstr;

hglb = GetClipboardData(CF_TEXT);

lpstr = (LPSTR)GlobalLock(hglb);

cch = lstrlen(lpstr);

if( cch > 0)

{

pwsz = new WCHAR[cch + 1];

MultiByteToWideChar(CP_ACP, 0, lpstr, -1, pwsz, cch);

pwsz[cch] = '\0';

agentSaveState();

agentPlay(L"Read");

agentSpeak(pwsz);

agentPlay(L"ReadReturn");

agentRestoreState();

delete pwsz;

}

GlobalUnlock(hglb);

CloseClipboard();

}

}

}

}

最后还须在程序退出时将当前窗口句柄从剪贴板监视队列移走:

void RemoveClipSpy()

{

ChangeClipboardChain(g_hMainWnd, g_hNextWnd);

}

(四) 与外部程序的接口

应用程序和AgentShell之间传递数据主要通过WM_COPYDATA消息实现,由于传递的数据类型各

有不同,所以需要定义一个数据结构来描述:

struct AgentActionSTRUCT

{

WORD nAction;

DWORD nD1;

DWORD nD2;

WCHAR sData[MAX_DATA_LEN];

};

nAction用来表示Agent应该执行的操作,如显示、表演等。nD1,nD2,sData用来记录传递的数据。

传递消息必须获取AgentShell主窗口的句柄,实现如下:

HWND GetAgentMainWnd()

{

return FindWindow(AGENT_CLASS_NAME, NULL);

}

由于Agent采用了UNICODE, 必须将ANSI字符转化为UNICODE字符:

BOOL SendMesToAgent(WORD nAction, DWORD nD1, DWORD nD2, LPCSTR sData)

{

UINT nSize;

HWND hWnd = GetAgentMainWnd();

if( hWnd)

{

action.nAction = nAction;

action.nD1 = (DWORD)nD1;

action.nD2 = (DWORD)nD2;

// 将ANSI符转换为UNICODE的字符

nSize = MultiByteToWideChar(CP_ACP, 0, sData, lstrlen(sData) + 1,

action.sData, MAX_DATA_LEN);

action.sData[nSize] = '\0';

//

COPYDATASTRUCT cds;

cds.dwData = (DWORD)0;

cds.cbData = (DWORD)sizeof(action);

cds.lpData = (VOID *)&action;

// 通过WM_COPYDATA消息与AgentShell交换数据

SendMessage(hWnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds);

return TRUE;

}

return FALSE;

}

目前AgentShell提供的函数主要有:

// 启动角色外壳程序(AgentShell) bRun是否执行

BOOL agentAPIRun(BOOL bRun = TRUE);

// 退出角色外壳程序(AgentShell)

BOOL agentAPIExit();

// 创建一个新角色(sPath角色数据文件路径, nLang主语言, nSubLang子语言)

BOOL agentAPICreate(LPCSTR sPath, UINT nLang, UINT nSubLang);

// 设置角色名字(sName角色名字)

BOOL agentAPISetName(LPCSTR sName);

// 将角色卸载

BOOL agentAPIUnload();

// 显示角色

BOOL agentAPIShow();

// 隐藏角色

BOOL agentAPIHide();

// 显示或隐藏角色

BOOL agentAPIShowORHide();

// 停止角色表演

BOOL agentAPIStop();

// 角色表演(sAction动作名称)

BOOL agentAPIPlay(LPCSTR sAction);

// 角色讲话(sText句子)

BOOL agentAPISpeak(LPCSTR sText);

// 角色鞠躬(x,y 指方向)

BOOL agentAPIGesAt(WORD x, WORD y);

// 移动角色到指定的位置(x,y移动的坐标)

BOOL agentAPIMoveTo(WORD x, WORD y);

// 保存当前角色显示状态

BOOL agentAPISaveState();

// 恢复角色的状态

BOOL agentAPIRestoreState();

// 允许自动阅读

BOOL agentAPIEnableAutoRead();

// 禁止自动阅读

BOOL agentAPIDisableAutoRead();

注意传递给AgentShell的数据长度不要超过1K(实际上一般不会大于1K)。

2) 使用接口

有了以上介绍的接口函数,对Agent的控制变的很简单,以下是一个简单的问候示例:

// 启动AgentShell

if( agentAPIRun(TRUE))

{

// 保存当前Agent的状态

agentAPISaveState();

// 开始表演

agentAPIPlay(_T("Greet"));

// 讲话

agentAPISpeak(_T("hello, my friend."));

// 表演结束

agentAPIPlay(_T("GreetReturn"));

// 恢复原来状态

agentAPIRestoreState();

// 退出

agentAPIExit();

}

AgentShell在笔者的免费软件"我的助手"中得到很好的利用,当然目前其仅涉及了Agent的一小部分内容,还有如语音识辨等,未做处理,还有待一步改进。以上程序在Visual C++ 6.0编译通过,源代码可到助手之家(http://www.helperHome.com)下载。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有