65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Windows 窗体中编程 Microsoft Agent

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (23投票s)

2009年4月26日

CPOL

17分钟阅读

viewsIcon

98294

downloadIcon

4

学习如何在 Windows 窗体应用程序中使用 Microsoft Agent 进行编程

这篇文章也可以在我的博客上找到,请在此处查看。

阅读此主题的阿拉伯语版本请在此处。 

目录 

  • 目录
  • 概述
  • Microsoft Agent 角色
  • Microsoft Agent SDK
  • Microsoft Agent Windows Forms 支持
  • 加载角色
  • 命令角色说话
  • 移动角色 
  • 角色动画
  • IAgentCtlCharacterEx 属性
  • AxAgent 控件事件
  • 创建自定义弹出菜单(NEW) 
  • 创建 Microsoft Agent 控制器
  • 释放资源
  • 结语
  • Microsoft Agent 兼容性(NEW
  • 下一步去哪里?
  • 实际应用示例 

概述

Microsoft Agent 是一项前所未有的技术,可以为应用程序和网页创建创新、新颖的对话式界面。它提供强大的动画功能、交互性和多功能性,并且开发起来极其容易。Microsoft Agent 是一项为人们提供与计算机进行更自然沟通方式基础的技术。它是一组软件服务,使开发人员能够将交互式动画角色集成到他们的应用程序和网页中。这些角色可以通过文本转语音 (TTS) 引擎或录制的音频说话,甚至可以接受语音命令。Microsoft Agent 使开发人员能够将用户界面扩展到当今普遍存在的传统鼠标和键盘交互之外。通过可视化的交互式个性来增强应用程序和网页,将同时拓宽和人性化用户与计算机之间的交互。开发人员可以为这些个性创建无限数量的角色和功能。

  • 一位欢迎主持人可以在用户首次开机、运行应用程序或浏览网站时问候新用户并提供引导式游览。
  • 一位友好的导师可以一步一步地指导用户完成一项任务或决策树。
  • 一位信使可以递送新电子邮件到达的通知或警报,然后主动为您朗读。
  • 一位助手可以为您执行任务,例如在 Internet 上查找信息,然后大声朗读。

尽管本课程专注于 Windows Forms,但您也可以在 Web 应用程序以及 XAML 应用程序中使用 Microsoft Agent。对于 Web 应用程序,您可以参考 Microsoft Agent Platform SDK 文档。对于 XAML 应用程序,它与 Windows Forms 相同,只是您需要将 Microsoft Agent 控件添加到 Sysetm.Windows.Forms.Integration.WindowsFormsHost WPF 控件中。

Microsoft Agent 角色

Microsoft Agent 角色可以在各种应用程序和网站中找到,包括 Office 2003 及其后续版本,它就是 Office 助手。图 1 显示了 Clippit Office 助手。图 2 显示了 Merlin,这是在网站和应用程序中最广泛使用的 Microsoft Agent 角色。

Figure 1 - Clippit Figure 2 - Merlin

有大量可供下载的角色。不过,Merlin 是 Windows 默认包含的。此外,Office 2003 附带了许多角色,包括 Clippit。

访问 Microsoft Agent Ring 并下载大量角色。

Microsoft Agent SDK

编程 Microsoft Agent 非常简单。但是,要开始编程,您需要下载 Microsoft Agent SDK。它及其文档可在 Microsoft 的 Microsoft Agent SDK 下载页面上下载。此外,您还需要下载语音 API 和文本转语音 (TTS) 引擎,以便启用 Microsoft Agent 的语音功能。这些软件包可在该页面上的 Speech SDK 中找到。最新版本是 5.1。

文本转语音 (TTS) 引擎用于将文本转换为语音。

值得一提的是,如果您需要扩展 Microsoft Agent 的语音功能以支持英语以外的其他语言,则需要从 Speech SDK 页面下载您喜欢的语言包。此外,您不必局限于使用为你创建的角色。如果您想创建自己的角色,可以下载 Agent Character Editor 并开始创建角色。请注意,为了让应用程序在用户的计算机上正确运行,用户需要安装两个软件包:Microsoft Agent 和 Microsoft TTS。请记住将它们作为先决条件包含在您的应用程序安装程序包中。下载并安装 Microsoft Agent SDK 后,您可能会注意到 Microsoft Agent SDK 提供了许多用于各种用途的组件。我们最感兴趣的组件是 ActiveX COM 库 AgentCtl.dll,它包含了 Microsoft Agent 对 Windows Forms 的支持功能。它包含 Microsoft Agent 控件,这是 Microsoft Agent Windows Forms 编程的入口。此组件以及许多其他组件位于 %windir%\MSAgent

Microsoft Agent Windows Forms 支持 

由于 AgentCtl.dll 是一个 ActiveX 组件,您不能直接在代码中使用它。您需要创建一个 RCW(运行时可调用包装器)组件(程序集),它将充当 .NET 和 COM 组件之间的桥梁。这会在 Visual Studio 中将 ActiveX 组件控件添加到工具箱时自动完成。另一种方法是使用 .NET Framework SDK 附带的 AxImp.exe 工具。

您可能会想,为什么我们没有使用 TlbImp.exe 工具?因为这是一个 ActiveX COM 组件,其中包含 ActiveX 控件。因此,我们使用了 AxImp.exe

由于 .NET 和 COM 不能直接相互调用,因此在空间中存在两种类型的包装器组件:RCW(运行时可调用包装器)和 CCW(COM 可调用包装器)。RCW 是充当 .NET 和 COM 之间桥梁的 .NET 程序集。相反,CCW 是充当 COM 和 .NET 之间桥梁的 COM 组件。

要在我们的 Windows Forms 应用程序中使用 Microsoft Agent,只需开始添加 Microsoft Agent 控件到我们的工具箱。图 3 显示了 COM 组件选项卡中的“选择工具箱项”对话框。

Figure 3 - Adding Microsoft Agent Control into Toolbox

此组件包含一个 Microsoft Agent 控件,该控件将自动添加到工具箱。将其拖放到窗体后,将自动向项目引用添加两个组件:AgentObjects AxAgentObjects。这些是 COM 组件的互操作 RCW 包装器组件。AxAgentObjects 是控件及其相关类型的包装器。另一方面,AgentObjects 组件是 COM 组件中其他类型的包装器。您可能会注意到,一个 AxAgentObjects.AxAgent 类型的方形控件已添加到窗体中。此控件在运行时将不可见。图 4 显示了窗体。

Figure 4 - Microsoft Agent Control

AgentCtl 使用 STA(单线程单元)线程模型。这意味着 COM 组件仅支持并绑定到单个线程。要为您的应用程序启用 STA,您需要使用 STAThreadAttribute 属性来修饰 Main() 函数。相反,MTA(多线程单元)线程模型意味着 COM 组件支持并可以被多个线程访问。如果未设置 STAThreadAttribute 或 MTAThreadAttribute 属性,则默认线程模型为 MTA。设置线程模型的另一种方法是使用 System.Threading.Thread.SetApartmentState 实例(与静态相对)方法。要了解 COM 组件支持 STA、MTA 或两者兼有,您可以查看注册表值 ThreadingModel 在 HKCR\CLSID\\InprocServer32

加载角色 

将 AxAgent 控件添加到窗体后,您现在可以命令该控件加载角色并显示它。AxAgent 控件包含一个 Characters 属性,该属性的类型为 AgentObjects.IAgentCtlCharacters ,它充当一个字典集合,包含已加载的角色及其名称。要加载角色,我们使用 Characters 集合的 Load() 方法。要获取已加载的角色,我们使用 Character() 方法,该方法返回一个 AgentObjects.IAgentCtlCharacterEx 类型的对象,该对象代表已加载的角色。以下是加载 Merlin 角色并显示它的代码。

// Here I'll specify the character name
// and the character location.
this.axAgent1.Characters.Load("Merlin",
    @"C:WindowsMSAgentcharsmerlin.acs");

AgentObjects.IAgentCtlCharacterEx character =
    this.axAgent1.Characters.Character("Merlin");

// A single argument contains a value to whether
// to animate the character while showing it
// or skipping the animation.
// You should set this to null or false.
// Set it to True to skip the animation.
character.Show(null);

除了 IAgentCtlCharacters 对象的 Load() Character() 方法外,您还可以使用 Unload() 方法来卸载给定的已加载角色。另外,IAgentCtlCharacterEx 支持用于隐藏角色的 Hide() 方法,这与 Show() 方法相反。

命令角色说话 

创建并显示角色后,您可以命令它使用文本转语音 (TTS) 引擎或录制的音频文件来说话。下面的代码段让角色首先使用编码字符串进行自我介绍,然后使用录制的音频文件。

character.Speak(
    "Hello everybody, I'm Merlin. " +
    "I'll guide you throuh the application windows."
    , null);

character.Speak(null, @"D:Recorded Introduction.wav");

您注意到,Speak() 方法可以将文本作为第一个参数,或将文件路径作为第二个参数。图 5 显示了 Merlin 在说话。

Figure 5 - Merlin Speaks

看一下 Think() 方法,它与 Speak() 非常相似。

移动角色 

您可以使用 MoveTo() 方法在屏幕上移动角色。

character.MoveTo(100, 100, null);

此方法接受三个参数,第一个和第二个参数是您希望将角色移动到的屏幕位置。第三个参数接受一个值,指示移动速度。如果此参数为 null,则使用默认值 1000。如果指定值为 0,角色将无动画地移动。显然,小于 1000 的值会减慢角色移动速度,大于 1000 的值会加快移动速度。

我曾有理由将此部分与动画部分合并。但我也有理由将其放在一个单独的部分中,我认为这是一种特殊的动画类型!

角色动画 

每个角色都有自己的动画。尽管许多角色共享许多动画名称,但这并非必需。每个动画都有一个名称。要获取角色支持的动画名称,您需要深入了解 AnimationNames 属性,该属性的类型为 AgentObjects.IAgentCtlAnimationNames ,它实现了 System.Collections.IEnumerable 接口。以下代码枚举了角色的动画名称。

IEnumerator enumerator =
    character.AnimationNames.GetEnumerator();
while (enumerator.MoveNext())
    this.listBox1.Items.Add(enumerator.Current.ToString());

现在,是时候和角色一起玩了。尝试以下代码。

character.Play("DoMagic1");
character.Speak("I'm better than Harry Potter. Am not I?", null);

图 6 显示了 Merlin 在施展魔法时说话。(骄傲的 Merlin)

Fogure 6 - Merlin Does Magic

请注意,包含方向(如左右)的动画名称是指角色的左右,而不是您的左右。

除了 Play() 方法外,角色对象还支持两个用于停止播放动画的方法:Stop()StopAll()Stop() 停止由其单个参数指定的动画。StopAll() 停止特定类型的所有动画。此方法有一个参数,可以接受“Move”、“Play”和“Speak”三个字符串值来停止移动、动画或说话命令。除了这三个值之外,StopAll() 还可以接受 null 值,这意味着停止所有类型的动画。

您可能已经注意到,角色对象的许多方法返回 AgentObjects.IAgentCtlRequest 类型的对象。此类型封装了发送给角色的请求。如果 IAgentCtlRequest.Status 非零,则操作失败,否则成功。请注意返回 IAgentCtlRequest 对象的方法,以及需要它作为参数的方法(如 Stop() 方法)。

另一种动画是向特定点致意。使用 GestureAt() 方法尝试此动画。

IAgentCtlCharacterEx 属性

此列表包含角色对象 (IAgentCtlCharacterEx) 最常见的属性。

  • AutoPopupMenu
    一个布尔属性,指示是否允许角色弹出菜单。弹出菜单是当用户右键单击角色或其任务栏图标(如果存在)时显示的菜单。默认值为 False。
  • Balloon
    这是一个只读属性,它本身包含只读属性,用于指示诸如角色说话时显示的氣泡的背景色等特征。此属性包含许多属性,其中大多数从其名称就可以自解释。
  • Left and Top
    控制角色在屏幕上的位置。(如果您想在移动角色的同时播放动画,最好使用 MoveTo() 方法而不是这两个属性)。
  • MoveCause
    一个只读属性,返回一个值,该值指示导致角色上次移动的原因。此属性可以返回以下 5 个值之一。
    • 0:
      角色尚未移动。
    • 1:
      用户移动了角色。
    • 2:
      您的应用程序移动了角色。
    • 3:
      另一客户端应用程序移动了角色。
    • 4:
      Agent 服务器将角色移动到屏幕上,以在屏幕分辨率更改后保持其在屏幕上。
  • Pitch
    一个只读属性,返回一个值,指示 TTS 的音高设置。
  • SoundEffectsOn
    将此属性设置为 True 以启用声音效果,设置为 False 则禁用。默认值为 True。
  • Speed: 一个只读属性,指示角色的速度。(此外,还有一些操作支持指定速度,例如 MoveTo() 方法)。
  • VisibilityCause
    一个只读属性,返回一个值,指示角色的可见状态。此属性可以返回以下 8 个值之一。
    • 0:
      角色尚未显示。
    • 1:
      用户通过角色任务栏图标弹出菜单中的命令或通过语音输入隐藏了角色。
    • 2:
      用户显示了角色。
    • 3:
      您的应用程序隐藏了角色。
    • 4:
      您的应用程序显示了角色。
    • 5:
      另一客户端应用程序隐藏了角色。
    • 6:
      另一客户端应用程序显示了角色。
    • 7:
      用户通过角色弹出菜单中的命令隐藏了角色。

Agent Server 是幕后控制 Microsoft Agent 的核心组件。它控制您的应用程序和其他应用程序及网站加载的所有角色。它负责处理每个 Microsoft Agent 角色的所有命令。值得一提的是,它包含在位于 %windir%\MSAgent 的可执行文件 AgentSvr.exe 中。当加载第一个角色时,此文件将自动启动,当卸载最后一个角色时,此文件将自动关闭。总而言之,您可以将 Agent Server 视为 Microsoft Agent 的核心内部组件。

AxAgent 控件事件 

该控件的一个奇怪之处在于,它定义了与角色相关的事件,而这些事件很可能位于角色对象本身。也许将它们放在这里是一个特性,因为 AxAgent 控制着角色的创建和销毁,但大多数时候您会觉得这很恼人。最常用的事件是。

  • BalloonShow 和 BalloonHide
    当已加载角色的氣泡显示或隐藏时发生。
  • ClickEvent 和 DblClick
    当已加载角色被单击或双击时发生。
  • DragStart 和 DragComplete
    当用户尝试拖动已加载的角色时发生。
  • ShowEvent 和 HideEvent
    当已加载角色显示或隐藏时发生,无论是用户还是您的代码执行的。如果您想检查是什么原因导致角色显示,请检查 IAgentCtlCharacterEx.VisibilityCause。作为回顾,请阅读上一节以了解此属性。
  • MoveEvent
    当已加载角色移动时发生,无论是用户移动它还是由您的代码移动。如果您想知道是什么导致角色移动,请参见上一节中的 IAgentCtlCharacterEx.MoveCause
  • RequestStart 和 RequestComplete
    当请求开始重新启动或完成后发生。请注意,许多方法接受并返回 IAgentCtlRequest 对象,这些对象定义了发送到服务器的请求。

创建自定义弹出菜单 

在您的应用程序中包含了所需的代理后,您是否对默认弹出菜单感到不满意?如果是,那么您来对地方了。

首先,创建您的 System.Windows.Forms.ContextMenuStrip 并添加所需的项目(好吧,可能包括“Hide”)并完成项目事件处理程序。

现在,让我们获取它。找到加载代理角色的代码(例如,调用代理控件对象 AxAgentObjects.AxAgentCharacters.Load() 方法),然后禁用角色对象 AgentObjects.IAgentCtlCharacterExAutoPopupMenu 标志/属性。此标志/属性决定是否允许默认弹出菜单。

例如,以下代码禁用此属性: 

AxAgentObjects.AxAgent agentCtl;
AgentObjects.IAgentCtlCharacterEx agentChar;

// initializing 'agentCtl'
// . . .

agentCtl.Characters.Load("Merlin", "merlin.acs");
agentChar = agentCtl.Characters.Character("Merlin");
agentChar.AutoPopupMenu = false; 

接下来是重点。当单击角色时,代理控件 (AxAgent) 的 ClickEvent 事件将被触发。因此,下一步是处理此事件并提供您的代码以弹出自定义上下文菜单。考虑以下代码。

// agentCtl.ClickEvent += agent_ClickEvent;

public void agentCtl_ClickEvent(object sender, AxAgentObjects._AgentEvents_ClickEvent e)
{
    if (e.characterID == "Merlin")  // check for this if you have many characters
    {
        if (e.button == 2) // 1 = left, 2 = right
        {
            myContextMenu.Show(e.x, e.y);
        }
    }
} 

创建 Microsoft Agent 控制器 

在处理 Microsoft Agent 时,有一些推荐的做法。其中之一是将 Microsoft Agent 实现代码与 UI 代码抽象出来,以实现可维护性和可用性目标,而这些目标可以通过一个控制器类来实现,该类封装了您所需的功能,并通过一次调用提供对许多操作的轻松访问。一个好的控制器类的例子如下。

internal sealed class AgentController
{
    private AgentObjects.IAgentCtlCharacterEx _char;
    private AxAgentObjects.AxAgent _agent;

    public AgentController(AxAgentObjects.AxAgent agent,
        string characterLocation)
    {
        _agent = agent;
        _agent.Characters.Load("CHAR", characterLocation);
        _char = _agent.Characters.Character("CHAR");
        _char.Show(null);
    }

    public bool IsVisible() { return _char.Visible; }
    public void Animate(string animation, bool stopAll)
    {
        if (stopAll)
            _char.StopAll(null);
        _char.Play(animation);
    }
    public void Speak(string text, bool stopAll)
    {
        if (stopAll)
            _char.StopAll(null);
        _char.Speak(text, null);
    }
    public void MoveTo(int x, int y, bool stopAll)
    {
        if (stopAll)
            _char.StopAll(null);
        _char.MoveTo((short)x, (short)y, null);
    }
    // Encapsulating many operations into one
    public void Surprise()
    {
        _char.StopAll(null);
        _char.Play("Alert");
        _char.Play("Sad");
    }
}

释放资源 

在使用 Microsoft Agent 完成工作后,您应该首先通过调用 AxAgent.Characters.Unload() 方法来卸载角色。其次,因为之前创建的 RCW(运行时可调用包装器)程序集的所有对象都只是非托管 COM 对象的一个包装器,它们会耗尽大量非托管资源,这些资源可能会长时间保留在内存中,因此您严格应该在使用完这些对象后立即将其处置。幸运的是,AxAgent 控件间接继承自 IDisposable 接口,因此您可以通过调用其 Dispose() 方法轻松处置它。相反,RCW 中的大多数对象(如角色对象 IAgentCtlCharacterEx)不实现此接口,因此您需要采取硬性方法,使用 Marshal 类来处置它。以下是执行此操作的代码。

while (System.Runtime.InteropServices.
    Marshal.FinalReleaseComObject(_char) > 0);

FinalReleaseComObject() 简单地尝试将 COM 对象的引用计数减到 0。您需要一遍又一遍地调用 FinalReleaseComObject() ,直到它返回 0,这意味着没有剩余的引用。

FinalReleaseComObject() 尝试一次性将引用计数减到 0,ReleaseComObject() 尝试逐步减小它。因此 ReleaseComObject() 需要比 FinalReleaseComObject() 更多的调用。而且 FinalReleaseComObject() ReleaseComObject() 快。递减对象的引用意味着处置它。

您已经学会了如何释放资源。但是,现在有一个问题。应该在何时处置对象?这取决于!您需要处置对象的时机取决于您对对象的使用。首先,如果您已完成与 AxAgent 控件的工作,并且在用户关闭您的应用程序之前不再需要它,建议您尽快处置它。否则,将 Dispose() 调用添加到窗体的受保护的 Dispose(bool) 方法中,以便在窗体关闭时处置它(老实说,就是处置)。以下代码片段演示了这一点。

protected override void Dispose(bool disposing)
{
    try
    {
        if (disposing)
        {
            this.axAgent1.Dispose();
            if (components != null)
                components.Dispose();
        }
    }
    finally { base.Dispose(disposing); }
}

此代码严格遵循处置设计指南。

您可以从窗体的代码编辑器窗口右上角的“成员”组合框中步入 Dispose() 方法。

像我这样的编程极端主义者(来自基地组织编程团队 :P)总是在处置他的对象,即使应用程序正在关闭。老实说,当应用程序关闭时,应用程序管理的所有资源和内存将立即释放,所以您实际上不需要在应用程序关闭时释放对象。

另一方面,对于 IAgentCtlCharacterEx 之类的某些对象,您应该采取 Marshal 类的方法。但是应该在哪里使用它呢?FinalReleaseComObject() 方法(或 ReleaseComObject() 也可以)的一个绝佳位置是在控制器类中。您可以让您的控制器类实现 IDisposable 接口,并在 Dispose() 方法中添加您的代码。请参阅以下示例。

为了清晰起见,代码已缩写。

internal sealed class AgentController : IDisposable
{

    ............

    public void Dispose()
    {
        _char.StopAll(null);
        _char.Hide(null);
        _agent.Characters.Unload("CHAR");
        while (System.Runtime.InteropServices.
            Marshal.FinalReleaseComObject(_char) > 0) ;
    }
}

这不是开玩笑,但您应该尽快处置控制器类,或者与窗体的处置一起,在处置 AxAgent 控件的同时处置它。

结语 

无需多言,您可以利用 Microsoft Agent 和 TTS 技术来制作提供友好用户导师的应用程序。您不必局限于使用现有的 Microsoft Agent 角色,您可以使用 Microsoft Agent Character Editor 创建自己的角色和动画,该编辑器可在 Microsoft Agent SDK 下载页面上下载。

Microsoft Agent 兼容性 

Microsoft 已宣布 Windows 7 将不兼容 Microsoft Agent。但是,您仍然可以安装 SDK 并正确运行您的应用程序。

下一步去哪里? 

Microsoft Agent 的良好参考资料包括:

  • Microsoft Agent SDK 文档。随 SDK 下载,请参阅顶部的“Microsoft Agent SDK”部分。
  • 书籍:Microsoft Agent Software Development Kit。
    ISBN:0-7356-0567-X
  • 书籍:Developing for Microsoft Agent。
    ISBN:1-5723-1720-5

实际应用示例

本课程不包含示例项目,它包含一个实际应用:Geming PartIt!用于将大文件分成小块,以便稍后合并,这样您就可以通过电子邮件发送它们或将它们存储在 CD 和软盘上。此应用程序使用 Microsoft Agent 角色 Merlin 为用户提供友好的应用程序指导。

我乐于收到关于我的应用程序(当然还有代码)的评论和反馈。 

© . All rights reserved.