使用Microsoft Agent的COM介面程式設計(轉)

ba發表於2007-08-15
使用Microsoft Agent的COM介面程式設計(轉)[@more@]Microsoft Agent具有相當廣泛的用途,我們既可以把它加入到普通應用程式中供本地系統使用,也可以把它嵌入到HTML文件中供 Internet/Intranet使用。Microsoft Agent支援C/C++、Visual Basic、Java、JScript和VBScript等多種程式語言,併為程式設計師提供了OLE自動化伺服器和ActiveX控制元件兩種程式設計方法,從本質上講,這兩種程式設計方法都屬於OLE技術的範疇,都建立在COM (Component Object Model,元件物件模型)的基礎之上。利用VC++的MFC類庫或VB等支援ActiveX的程式設計工具可以很容易地呼叫ActiveX控制元件,但是 ActiveX控制元件把許多OLE技術的細節都隱藏起來了,如果我們想加深對一個COM物件的瞭解的話,則應直接使用它的COM介面來程式設計,從這一點出發,本文將介紹Microsoft Agent的COM介面的基本程式設計方法,希望能夠起到拋磚引玉的作用。

OLE程式設計的基礎知識

早期的OLE(現稱為OLE 1)首次出現在Windows 3.1中,其主要用途是生成複合型文件,使得一個應用程式的文件可以透過鏈連或嵌入的方式包含其它應用程式的資料(物件)。隨著軟體元件技術變得日益重要起來,Microsoft在OLE 1

的基礎上設計了OLE 2,利用它可以實現二進位制級上可重用的軟體元件,並且控制這些元件的版本和擴充其功能變得相當容易。由於OLE 2的體系結構被設計成為開放式的、可擴充的,所以以後不會再出現OLE 3或4。經過多年的發展,如今的OLE已經包括了OLE自動化、COM、COM+、DCOM和ActiveX等多項技術,它們是 ActiveDirectory(將用於NT 5.0的一項關鍵技術)、OLE Messaging、DirectX 、Active Controls、ActiveX Scripting和Task Scheduler等等多種新技術的基礎,OLE已不再是Object Linking and Embedding的縮寫,它變成了一個獨立的單詞,專門用來表示Microsoft的軟體元件整合技術。

COM 是OLE技術的基礎,它規定了物件之間如何相互通訊,符合COM規範的物件也叫做COM物件。按照COM的規定,物件內部可以使用任何語言來編寫,它們透過介面(Interface)來與外界通訊。所謂介面是指物件提供的一組特定的功能呼叫(方法),每個物件可以有多個介面,不同的物件可以實現同一種介面,客戶程式透過物件的介面指標來呼叫物件的功能。由於OLE規定了元件在二進位制級上可重用,客戶程式不能夠直接訪問物件內部的資料,讀取或設定物件的屬性也都要透過介面來進行。每一種介面都是從一個叫IUnknown介面繼承而來,都必須重新實現IUnknown的三個方法: QueryInterface、AddRef和Release,客戶程式呼叫QueryInterface可以獲得物件的其它介面指標,AddRef和 Release分別將物件的引用計數加一和減一,當引用計數為零時,物件就會被釋放。客戶程式呼叫COM物件的一般步驟是首先建立一個物件,然後獲取需要的介面指標,呼叫相應的功能,最後釋放介面指標和物件。

C++程式呼叫Microsoft Agent的基本方法

根據前面介紹的基礎知識,下面我們來看看C++程式中如何呼叫Microsoft Agent。

1.設定與選項
本文使用的程式設計工具為Visual C++ 5.0,程式為一般的Win32應用程式,為了使程式能夠正確地編譯連線和執行,您首先需要擁有AgtSvr.h和AgtSvr—i.c兩個定義 Microsoft Agent的COM介面的檔案,它們可以在Microsoft的MS Agent站點(
prog/agent/)上找到,或者請下載 Microsoft最新的Internet Client SDK或Platform SDK,其次,請在Project/Settings/Link選單中加入以下的庫:ole32.lib、oleaut32.lib、uuid.lib、 odbc32.lib odbccp32.lib,最後要確保系統中安裝有Microsoft Agent及動畫人物資料。

2.建立Microsoft Agent物件
建立OLE物件之前需要初始化OLE,這由OleInitialize()函式來完成,如果OLE初始化不成功,那麼就無法繼續執行後面的程式碼,建立物件由CoCreateInstance()函式來完成:
if (FAILED(OleInitialize(NULL))) return -1;//初始化OLE
hRes = CoCreateInstance(CLSID—AgentServer,NULL,CLSCTX—SERVER,IID—IAgent,(LPVOID *)&pAgent);//建立Microsoft Agent Server的例項
if (FAILED(hRes)) return -1;

CoCreateInstance ()的第一個引數是物件的CLSID(類程式碼),Microsoft Agent Server的CLSID為定義在AgtSvr—i.c檔案中的CLSID—AgentServer,這個128位的編碼唯一地標識了Agent伺服器,伺服器所在路徑和執行引數等資訊都放在系統登錄檔中;第二個引數一般情況下設為NULL;第三個引數用來指明物件的執行環境,如遠端或本地,此處設為 CLSCTX—SERVER;第四個引數指明用來與物件通訊的介面的ID,這也是一個128位的編碼,Agent的介面ID為IID—IAgent;第五個引數是用來接收IAgent的介面指標。

如果Microsoft Agent Server還沒有在記憶體中執行,那麼CoCreateInstance()會啟動它並建立一個Agent物件,如果伺服器已經執行了,則 CoCreateInstance()會與之連線並建立一個Agent物件。當所有的Agent物件都被釋放了後,伺服器自動退出。

3.裝入動畫人物資料
下面的程式碼呼叫IAgent::Load()方法來裝入一個動畫人物的資料,由於Agent伺服器在自己的記憶體空間中執行,所以傳送的字串變數需要用SysAllocString()來分配記憶體:

VariantInit(&vPath); //初始化OLE變數
vPath.vt = VT—BSTR; //指明變數型別為Unicode的字串
vPath.bstrVal=SysAllocString(kpwszCharacter);
//kpwszCharacter為動畫人物資料的存放路徑
hRes = pAgent->Load(vPath,&lCharID,&lRequestID);
//裝入資料,人物ID在lCharID中返回
hRes = pAgent->GetCharacter(lCharID,&pdCharacter);
//獲取lCharID的IDispatch介面指標呼叫IDispatch::QueryInterface()方法可以得到 //IAgentCharacter的介面指標:
hRes = pdCharacter->QueryInterface(IID—IAgentCharacter, (LPVOID *)&pCharacter);
pdCharacter->Release(); //釋放IDispath

透過IAgentCharacter介面就可以呼叫動畫人物支援的各種方法了:
hRes = pCharacter->Show(FALSE, &lRequestID);//顯示動畫人物
hRes = pCharacter->MoveTo(320,240,100,&lRequestID); //移動動畫人物到螢幕中央
bszSpeak = SysAllocString(L"Hello World!"); //分配字串
hRes = pCharacter->Speak(bszSpeak, NULL, &lRequestID); //讓動畫人物說話
SysFreeString(bszSpeak); //釋放字串所佔記憶體

4.釋放物件
程式在退出之前需要把建立的Agent物件釋放:
if (pCharacter) {
pCharacter->Release(); //釋放IAgentCharacter介面
pAgent->Unload(lCharID); //解除安裝動畫人物資料
}
pAgent->Release(); //釋放Agent物件
VariantClear(&vPath); //清除OLE變數

進一步的程式設計要點

前面介紹的是呼叫Microsoft Agent伺服器最基本的一些步驟,為了完成比較實際的任務,客戶程式還應根據自己的情況考慮下面的一些程式設計要點。    1.檢查Agent Server的版本

OLE 要求元件或物件具有向後相容性,高版本物件支援低版本物件的所有介面和屬性,這樣可以很方便地進行元件升級。客戶程式通常應檢查物件的版本,只有系統中安裝的物件的版本號高於或等於所期望的版本號時才能呼叫物件。下面的IsValidAgentVersion()函式檢查Microsoft Agent的版本號,並將它與定義在AgtSvr.h檔案中的版本號相比較:
  BOOL IsValidAgentVersion(IAgent *pAgent) {
  IDispatch *pdAgent = NULL;
  ITypeInfo *pTypeInfo = NULL;
  ITypeLib *pTypeLib = NULL;
  TLIBATTR *pTypeLibAttr = NULL;
  BOOL bValid = FALSE;
  UINT uiIndex;
  pAgent->QueryInterface(IID—IDispatch, (LPVOID *)&pdAgent);
  pdAgent->GetTypeInfo(0, 0, &pTypeInfo); //取得型別資訊
  pTypeInfo->GetContainingTypeLib(&pTypeLib, &uiIndex);//取得型別庫
  pTypeLib->GetLibAttr(&pTypeLibAttr); //取得型別庫中的屬性
  if ((pTypeLibAttr->wMajorVerNum > AGENT—VERSION—MAJOR) ||((pTypeLibAttr->wMajorVerNum == AGENT—VERSION—MAJOR)&&(pTypeLibAttr->wMinorVerNum >= AGENT—VERSION—MINOR)))
  bValid = TRUE; //期望的版本號定義在AgtSvr.h檔案中
  if (pTypeLib) {
  if (pTypeLibAttr) pTypeLib->ReleaseTLibAttr(pTypeLibAttr);
  pTypeLib->Release(); }
  if (pTypeInfo) pTypeInfo->Release();
  if (pdAgent) pdAgent->Release();
  return bValid;}

2.實現IAgentNotifySink介面
為了能夠處理使用者的輸入,瞭解Agent物件的狀態,客戶程式應實現IAgentNotifySink介面來接收Agent物件的事件。 IAgentNotifySink的宣告和預設實現可以在Platform SDK或Internet Clinet SDK中的Notify.h和Notify.cpp中找到,客戶程式應根據需要修改某些事件的處理函式
。下面的程式碼向Agent物件註冊IAgentNotifySink介面,其中AgentNotifySink是從IAgentNotifySink繼承而來:

pSink = new AgentNotifySink;
pSink->AddRef(); //增加引用計數
hRes = pAgent->Register((IUnknown *)pSink, &lNotifySinkID);//進行註冊
...
if (pSink) {
pAgent->Unregister(lNotifySinkID); //登出IAgentNotifySink介面
pSink->Release(); }

客戶程式最感興趣的兩個事件是RequestComplete和Command。Agent伺服器採用非同步方式來處理客戶程式的各種請求,這樣客戶程式可以在請求服務的同時進行自己的工作,當伺服器完成一項請求時就會激發RequestComplete事件,客戶程式可以判斷是哪一項請求已經結束,並做相應的處理。Command事件是當使用者使用滑鼠或麥克風向動畫人物發出命令時激發的,客戶程式可以透過IAgentUserInput介面來了解命令的具體資訊。

3.自定義命令
Agent伺服器為每個動畫人物都提供了一些預設的命令,這些命令出現在關聯選單或命令視窗中,客戶程式可以透過IAgentCommands介面新增自定義命令。

為了得到IAgentCommands的介面指標,應使用引數IID—IAgentCommands來呼叫IAgentCharacter:: QueryInterface(),IAgentCommands的Add()或Insert()方法可以加入自定義命令,同時設定Caption、 Visible和Voice屬性,指明該命令是否顯示出來,顯示在關聯選單中還是在命令視窗中。

Agent伺服器為每個命令賦予一個ID值,客戶程式可以使用這個ID值呼叫IAgentCommands::GetCommand()方法,得到每個命令的IAgentCommand介面指標,從而對單個命令的各種屬性進行調整。

4.WAV檔案代替語音合成
Microsoft Agent目前只支援英語的語音合成功能,要輸出中文時只能用WAV檔案來代替。如果給IAgentCharacter::Speak()方法的第二個引數傳遞一個WAV檔案的路徑,那麼Agent伺服器自動播放這個WAV檔案,並在文字氣球中顯示出第一個引數中包含的文字,如果給第二個引數傳遞一個帶音節資訊的LWV檔案的路徑,則不需要在第一個引數中提供文字,因為LWV檔案中包含有文字資訊。當使用LWV檔案時,動畫人物的嘴部動作能與輸出的語音保持一致,所以在可能的情況應該儘量使用LWV檔案,這種格式的檔案可用Microsoft Agent Linguistic Information Sound Editing Tool編輯WAV檔案來生成。

5.其它一些COM介面
除了前面提到的介面外,Agent伺服器還有其它一些COM介面。IAgentCommandWindow允許客戶程式訪問或設定命令視窗的屬性,包括位置、大小和是否可見。 IAgentSpeechInputProperties允許客戶程式訪問語音輸入功能的屬性,其中大部分屬性都是隻讀的。 IAgentAudioOutputProperties允許客戶程式讀取語音輸出功能的部分屬性。IAg
entPropertySheet允許客戶程式訪問或設定Agent伺服器的屬性表。IAgentBalloon允許客戶程式訪問文字氣球的屬性,可以設定少數屬性,如是否可見和字型名稱。關於這些介面的具體定義和用途請參考Microsoft Agent的幫助文件。

總 結

Microsoft Agent是一項較新的技術,它屬於OLE的範疇,涉及較深的程式設計理論,本文所介紹的只是從OLE自動化服務角度出發的最基本的使用方法。

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

相關文章