Microsoft Agent; Providing a Custom Popup Menu

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

A second honeymoon with Microsoft Agent. Do you remember our article Programming Microsoft Agent in WinForms and our sample application PartIt? After you have included your desired agent in your application, are you feeling bad with the default popup menu? If so, then you are in the right place (welcome :).)

Enough talking, let’s get to work! First, prepare your code that loads the Microsoft Agent and brings it to the screen.

After that, create your System.Windows.Forms.ContextMenuStrip and add your required items (well, including ‘Hide’ maybe) and finish the item event handlers.

Now, let’s complete it. Get to the code that loads the agent character (e.g. calls the Characters.Load() method of the agent control object, AxAgentObjects.AxAgent) and just disable the AutoPopupMenu flag/property of the character object, AgentObjects.IAgentCtlCharacterEx. This flag/property determines whether to allow the default popup menu or not.

For example, the following code disables this property:

    AxAgentObjects.AxAgent agentCtl;
    AgentObjects.IAgentCtlCharacterEx agentChar;

    // initializing 'agentCtl'
    // . . .

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

Next comes the interesting point. When the character is clicked, the ClickEvent event of the agent control (AxAgent) fires. So the next step is to handle this event and to provide your code that brings up your custom context menu. Consider the following code:

// 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);
        }
    }
}

Well done!

Have a nice Sunday!

Creating a Simple Sheet Designer in C#

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Code: Geming.Sisc.msi

Contents

Contents of this article:

  • Contents
  • Introduction
  • Problem
  • Requirements
  • Solution
  • Snapshots
  • Component Design
  • Class Diagrams
  • Database Diagram
  • Characteristics
  • Background
  • Code Highlights
  • Download

Introduction

Today, we are going to create a simple application, actually a simple sheet designing tool. This tool gives the user the flexibility to design his sheets, reports, bills, invoices, and receipts (whatever.)

In this writing, we will refer to reports, bills, invoices, receipts, etc. with just the name sheets. For this, we will need to give a sheet a definition.

A sheet is just like an empty page (i.e. template) that contains sheet items. For example, a resume template is a sheet that has many items (name, birth date, address, etc.) A cash receipt is a sheet that has a few items (date, amount, charged to, received by, etc.)

We will go through this tool in a nice way. We will begin by a very simple system analysis and application design. After that, we will get into coding.

Actually, I€™m not an architect, and I think I will never be. So please, DO NOT blame me for this bad analysis. It is just an illustration to get you have a solid understanding of what our application is designed for.

Problem

The user is tied into the sheet designs that the developer has created for him. The user should have the ability to design his own sheets and/or to edit application defined sheets.

Requirements

User Requirements

The following is list of common user requirements:

  • The application should allow the user to create, edit, and delete sheets, and to group those sheets into categories he creates.
  • For the sake of simplicity, those categories would be only one level, no nested categories are allowed.
  • The application should be persistent. The data should be stored in a database and loaded when the user needs it.
  • The sheet should be loaded with right data and printed whenever the user asks.
  • Like Visual Studio, the user should have a Toolbox that has all types of sheet items. The user could insert a sheet item into his sheet by drawing it into the screen, dragging it from the toolbox, or just double-clicking its icon in the toolbox.
  • The user should have a ruler to allow him measuring sheet item dimensions. In addition, he should have a design-time-only grid.
  • The user should be allowed to set attributes of the sheet like the page setup, grid settings, background color, etc.
  • Not all sheet attributes are printed out; some are for design-mode only (like background color, and grid.)
  • Each type of sheet items should have its own attributes and the user should be allowed to change them too.
  • The user should be allowed to drag sheet items around the screen and position them.
  • The user should be allowed to cut, copy, paste, and delete sheet items.
  • Of course, the user should be allowed to print out the sheet, and to preview it too.
  • The application should be generic to be used in any system and in any application.
  • Other pieces of the system should have the possibility to interact with the sheet.

Functional Requirements

Common functional (developer) requirements are:

  • Abstraction. Component/Class abstraction should be considered into the design. The system should consist of several components each of which groups a related features together (e.g. interface objects, business objects, and data management.)
  • Extensibility. The system should be extensible. A good class hierarchy should be created, and derivation (inheritance) should be taken into consideration. In addition, every sheet item should be represented by a class in this hierarchy.
  • Technically, sheet items would be called shapes. Because they are just drawings into the sketch (or sheet.)
  • To allow the user to work with sheet items (shapes) and the application to populate the sheet with data, sheet items should not be drawn directly into the sheet page. Instead every item should represented by a class that has its own drawing routine.
  • The sheet should be a container for the shapes. All shapes are children of the sheet.
  • The sheet and rulers should be Windows controls to allow them to be hosted by a Windows Form.
  • Sheet items should be Windows controls too to allow them to be hosted by the sheet.
  • The sheet items should be owner-drawn controls and they should not be derived from existing Windows controls to help integrating them easily into the sheet.
  • For the sake of simplicity, the sheet and shapes should be serialized into XML and stored in the database.
  • The database should be a SQL Server database to allow faster communication and easier manipulation of XML data.
  • The database should have three tables for the three core system components, the category, the sheet, and the shape.
  • Every sheet item in a given sheet should have a unique name (or a tag) to allow other system components to interact with it (e.g. populate it with the right data.)
  • The developer should have the ability to control the quality of the painting process for each control or item independently.

Solution

After going through project requirements and considering a sophisticated system design, we got a nice plan for our project that would be called Geming SISC (Sheet Infrastructure Components.)

This project would be created using C# and .NET 2.0 (or future versions of course.)

Snapshots

The following are final snapshots of the application. Figure 1 shows a cash receipt sheet designed by labels, boxes, and lines. Figure 2 shows a simple resume template designed by only a few labels and a picture. The left pane of the application lists current sheets grouped by their categories.

Figure 01 - Geming SISC (Cash Receipt)
Figure 01 - Geming SISC (Cash Receipt)
Figure 2 - Geming SISC (Resume Template)
Figure 2 - Geming SISC (Resume Template)

As you know other system components could fill those fields up with correct information.

Component Design

Figure 3 shows the three components of our system.

Figure 3 - Geming SISC Component Design
Figure 3 - Geming SISC Component Design

The three core components of our system, Geming SISC are:

  • Geming.Sisc.Infrastructure:
    Contains the sheet control and shapes controls (label, text box, etc.) The sheet, rulers, and sheet items controls are all derived from System.Windows.Forms.Control.
  • Geming.Sisc.Data:
    Business objects that would be sent to the data among database manager objects. This component references the Geming.Sisc.Infrastructure component.
  • Geming.Sisc.Driver:
    The application interface that would be used to design sheets. It references the other two components.

Class Diagrams

Extensibility is one of the main goals of the system. A flexible class hierarchy should be considered as well as derivation (i.e. inheritance) of controls for other external objects.

The following is the class hierarchy for the sheet and the ruler controls.

Figure 4 - Sheet and Rulers Class Hierarchy
Figure 4 - Sheet and Rulers Class Hierarchy

As we can see, the base class is the System.Windows.Forms.Control class. This allows the sheet and rulers controls to be inserted into a Windows form or a Windows control.

The following figure, figure 5, shows the class hierarchy of sheet items (technically called shapes.)

Figure 5 - Sheet Items (Shapes) Class Hierarchy
Figure 5 - Sheet Items (Shapes) Class Hierarchy

As we can see, the base class for all shapes is the abstract Geming.Sisc.Infrastructure.ShapeBase class which inherits from System.Windows.Forms.Control class. All other shapes are derived from ShapeBase.

Two edit shapes were created, TextBoxShape that has a look like a Windows Text Box, and LabelShape that has a look like a normal Windows Label. Both are derived from the abstract EditableShapeBase.

Other shapes are BoxShape, LineShape, ImageShape, and CheckBoxShape.

All classes are serializable (implement System.Runtime.Serialization.ISerializable interface,) so they can easily converted into XML and pushed to the database.

For simplicity, we have developed just the box, line, image, check box, and two edit shapes (or sheet items.) You can go further and create any other shapes you like. In addition, you can create more specific items like CurrencyField, DateField, etc.

For more detailed class diagrams like those shows class members, check the application code.

Database Diagram

In its simplicity, database is defined as the following diagram illustrates:

Figure 6 - Geming SISC Database Diagram
Figure 6 - Geming SISC Database Diagram

Notice that, all data operations are carried out through stored procedures in the database.

The Shape.Value column is of the type xml to allow easier manipulation of XML data in the future.

Characteristics

Here are some of the characteristics (i.e. attributes) of the sheet (some are represented by properties):

  • Background color:
    The user should be able change the background color. Notice that the background color is not printed.
  • Non-printable grid:
    To help the user position sheet items. The user should be able to display them or not. In addition, the user can change the grid color.
  • Margin:
    The application should set the page margin based on print settings.
  • Title:
    Every sheet has a title, a description, and a category.

In addition, next is a list of some the characteristics of a shape (sheet item):

  • Selected:
    Is the shape currently selected or not. The user could select a shape by the mouse. A selection frame is drawn around the shape when selected.
  • Non-printable size grip:
    Each shape should have a size grip to allow the user to resize the shape.
  • Cloning:
    The shape should be able to be cloned; that is copied, to allow the copy and paste feature.

Background

Here are some refreshers of techniques we use in this system:

  • Painting

We will rely on custom painting in most situations. Therefore, well request help from System.Drawing classes specially the System.Drawing.Graphics class.

In some shapes like those mimic existing Windows controls (like the text box and the check box,) we will get help from classes in System.Windows.Forms.VisualStyles namespace to reflect the visual styles of Windows in our controls. In addition, System.Windows.Forms.ControlPaint class is used for drawing borders and selection rectangles.

  • Painting Quality

Every control in our project (sheet, rulers, and shapes) has a paining quality property that determines the quality of the drawing (low, medium, and high.) For this to work we will make use of some properties of the System.Drawing.Graphics object like those related to smoothing and anti-aliasing feature.

  • Serialization

All serialized objects should be marked with System.SerializableAttribute attribute. With help from System.Runtime.Serialization namespace we could customize the serialization process.

The core interface that would allow us to customize the serialization process is the System.Runtime.Serialization.ISerializable interface (implemented in all serializable classes.) Notice that, we should add the deserialization constructor to get correct deserialization.

  • Database

The database is a SQL Server client database with all operations included as stored procedures.

The system uses two-tier architecture; means that it accesses the database directly using just three objects, a connection, a command, and a data reader.

  • Design-Mode Support

For extending design-mode support we have created custom designer service for the Sheet class that inherits the System.Windows.Forms.Design.ParentControlDesigner class to allow nested controls in the Sheet object in the design-mode.

Code Highlights

In this section we will talk about significant blocks of code that would be of interest.

  • Painting Routines

The following is the code for OnPaint() routine overridden by only the ShapeBase class.

Listing 1 – ShapeBase.OnPaint() Method Code Listing
protected override void OnPaint(PaintEventArgs e)
{
    GraphicsManager.SetQuality(e.Graphics, PaintingQuality);

    ControlPaint.DrawBorder(e.Graphics, this.ClientRectangle, this.ForeColor, ButtonBorderStyle.Dotted);

    PaintShape(e.Graphics);

    if (Selected)
        DrawSelectionFrame(e.Graphics);

    ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, GetResizeGripRect());
}

This function first calls the GraphicsManager.SetQuality() function that sets the quality attributes of the Graphics object. We will get back to this function in about a moment.

After that the function paints the control border using the System.Windows.Forms.ControlPaint class.

Next comes the interesting point. The function calls the virtual function PaintShape() that a derived class overrides to provide its own drawing routines.

For example, the TextBoxShape class has this PaintShape() override:

Listing 2 – TextBoxShape.PaintShape() Method Code Listing
public override void PaintShape(Graphics dc)
{
    base.PaintShape(dc);

    using (Brush b = new SolidBrush(this.BackColor))
    dc.FillRectangle(b, this.ClientRectangle);

    using (Pen p = new Pen(SystemColors.ActiveCaption, 1))
    {
        p.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
        Rectangle r = this.ClientRectangle;
        dc.DrawRectangle(p, r);
    }

    Rectangle rect = this.ClientRectangle;
    rect.Offset(2, 2);
    rect.Width -= 4; rect.Height -= 4;

    StringFormat format = new StringFormat();
    format.LineAlignment = this.Alignment;
    if (this.RightToLeft == RightToLeft.Yes)
        format.FormatFlags = StringFormatFlags.DirectionRightToLeft;

    using (Brush b = new SolidBrush(this.ForeColor))
        dc.DrawString(this.Text, this.Font, b, rect, format);
}

After that the OnPaint() function draws selection frame if the shape is currently selected.

Just before the function closes, it calls the ControlPaint.DrawSizeGrip() function to draw the sizing grip that the user would resize the shape from.

  • Painting Quality

In our library Geming.Sisc.Infrastructure, we have created a helper class, GraphicsManager, which contains only one function, SetQuality().

This function accepts two input parameters, the graphics object and the quality you wish to set to that object. The following is the code for the SetQuality() function.

Listing 3 – GraphicsManager.SetQuality() Method Code Listing
public static void SetQuality(System.Drawing.Graphics g, PaintingQuality quality)
{
    if (g == null)
        throw new ArgumentNullException("g");

    if (quality == PaintingQuality.High)
    {
        g.CompositingQuality = CompositingQuality.AssumeLinear;
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.PixelOffsetMode = PixelOffsetMode.Half;
        g.SmoothingMode = SmoothingMode.AntiAlias;
        g.TextRenderingHint =
        System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
    }
    else if (quality == PaintingQuality.Medium)
    {
        g.CompositingQuality = CompositingQuality.HighQuality;
        g.InterpolationMode = InterpolationMode.Bilinear;
        g.PixelOffsetMode = PixelOffsetMode.HighQuality;
        g.SmoothingMode = SmoothingMode.HighQuality;
        g.TextRenderingHint =
        System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
    }
    else
    {
        g.CompositingQuality = CompositingQuality.Default;
        g.InterpolationMode = InterpolationMode.Default;
        g.PixelOffsetMode = PixelOffsetMode.Default;
        g.SmoothingMode = SmoothingMode.Default;
        g.TextRenderingHint =
        System.Drawing.Text.TextRenderingHint.SystemDefault;
    }
}

Download

The application Geming.Sisc along with its code is available for download here.

Download Geming.Sisc

Programmatically Taking a Screen Snapshot

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Honestly, this lesson is not primarily focusing on how to take a screen snapshot! Instead, it is focusing on how to simulate keyboard strokes and send them to the active application.

In .NET, this is done using the System.Windows.Forms.Forms.SendKeys class. As you might guess it is located in System.Windows.Forms.dll.

Using the SendKeys class you can call static methods like SendWait() to send the keystrokes and wait for them to be processed, or you can send them via the Send() method if you do not care about whether they processed or not.

Both Send() and SendKeys() requires a single argument keys. This argument represents the keys to send it.  Each key is represented by one or more characters. To get list of all values supported for this argument visit the documentation for the SendKeys class, visit this page.

For our example we will try to combine two keys Alt + Print Screen. Alt represented by a percent (%) sign. Print Screen key is represented by the value PrtSc enclosed in curly brackets.

// C#

public static Image TakeScreenSnapshot(bool activeWindowOnly)
{
    // PrtSc = Print Screen Key
    string keys = "{PrtSc}";

    if (activeWindowOnly)
        keys = "%" + keys; // % = Alt

    SendKeys.SendWait(keys);

    return Clipboard.GetImage();
}
' VB.NET

Public Shared Function TakeScreenSnapshot(activeWindowOnly As Boolean) As Image
    ' PrtSc = Print Screen key
    Dim keys As String = "{PrtSc}"

    If (activeWindowOnly) Then
        keys = "%" & keys
    End If

    SendKeys.SendWait(keys)

    Return Clipboard.GetImage()
End Function

Programming Microsoft Agent in Windows Forms

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

App: Geming.PartIt.msi
Code: Geming.PartIt (Source).msi

Contents

Contents of this article:

  • Contents
  • Overview
  • Microsoft Agent Characters
  • Microsoft Agent SDK
  • Microsoft Agent Windows Forms Support
  • Loading Characters
  • Ordering the Character to Speak
  • Moving the Character
  • Character Animation
  • IAgentCtlCharacterEx Properties
  • AxAgent Control Events
  • Creating a Custom Popup Menu
  • Creating a Microsoft Agent Controller
  • Freeing up Resources
  • The Last Word
  • Microsoft Agent Compatibility
  • Where to Go Next?
  • A Real-World Example

Overview

Microsoft Agent is an unprecedented technology to create innovative, new conversational interfaces for applications and Web pages. It provides powerful animation capability, interactivity, and versatility, with incredible ease of development.

Microsoft Agent is a technology that provides a foundation for more natural ways for people to communicate with their computers. It is a set of software services that enable developers to incorporate interactive animated characters into their applications and Web pages. These characters can speak, via a Text-to-Speech (TTS) engine or recorded audio, and even accept spoken voice commands. Microsoft Agent empowers developers to extend the user interface beyond the conventional mouse and keyboard interactions prevalent today.

Enhancing applications and Web pages with a visible interactive personality will both broaden and humanize the interaction between users and their computers.

There are a limitless number of roles and functions that developers can create for these personalities to perform.

  • A welcome host could greet new users and provide a guided tour the first time a computer is turned on, an application is run, or a Web site is browsed.
  • A friendly tutor could lead someone through a task or a decision tree with instructions step-by-step along the way.
  • A messenger could deliver a notification or alert that a new e-mail has arrived and then offer to read it to you.
  • An assistant could perform tasks for you like looking up information on the Internet and then reading it out loud.

Although, this lesson focuses on Windows Forms, you can take advantage of Microsoft Agent in Web applications, and also XAML applications. For Web application, you can refer to the Microsoft Agent Platform SDK documentation. For XAML applications, it is the same as Windows Forms, except that you will need to add Microsoft Agent Control inside a Sysetm.Windows.Forms.Integration.WindowsFormsHost WPF control.

Microsoft Agent characters

Microsoft Agent characters can be found in various applications and Websites, including Office 2003 and its ascendants, it comes as the Office Assistant. Figure 1 shows the Clippit office assistant. And figure 2 shows Merlin the most widely-used Microsoft Agent character in Websites and applications.

Figure 1 - Clippit
Figure 1 - Clippit
Figure 2 - Merlin
Figure 2 - Merlin

There’re plenty of characters available for download. Though, Merlin is included by default in Windows. In addition, Office 2003 comes with many characters including Clippit.

Visit Microsoft Agent Ring for downloading plenty of characters.

Microsoft Agent SDK

Programming Microsoft Agent is very easy. But, in order to start programming you need to download the Microsoft Agent SDK. It’s available for downloadalong  with its documentation in the Microsoft Agent SDK download page on Microsoft. In addition, you need to download the Speech API and Text-to-Speech (TTS) Engine in order to enable the speech feature of Microsoft Agent. These packages are available in the Speech SDK in this page. The most recent version is 5.1.

The Text-to-Speech (TTS) Engine is used to translate text into voice.

It is worth mentioning that, if you need to extend speech feature of Microsoft Agent to support other languages other than English, you need to download your preferred language package from the Speech SDK page.

In addition, you are not end up using characters created for you. If you need to create your own characters you can download the Agent Character Editor and start using character creation.

Take into consideration that, for the application to run correctly on user’s machine, the user need to install two packages, Microsoft Agent and Microsoft TTS. Remember to inculde them as prerequisites in your application installer package.

After downloading and installing the Microsoft Agent SDK, you may notice that Microsoft Agent SDK offers you many components for many purposes. The most component that we are interested in is the ActiveX COM library AgentCtl.dll that contains the Microsoft Agent support features for Windows Forms. It contains the Microsoft Agent Control that’s the door for Microsoft Agent Windows Forms programming. This component -and many others- resides on %windir%MSAgent.

Microsoft Agent Windows Forms Support

Because AgentCtl.dll is an ActiveX component you cannot use it directly in your code. You need to create a RCW (Runtime-Callable Wrapper) component (assembly) that will act as a bridge between .NET and the COM component. This is done automatically while adding the ActiveX component control to your toolbox in Visual Studio. Another way is to use the AxImp.exe tool that comes with the .NET Framework SDK.

You might be wandering why we haven’t used the TlbImp.exe tool? Because this is an ActiveX COM component contains ActiveX controls. Therefore, we used the AxImp.exe.

Because .NET and COM cannot call each other directly, two types of wrapper components exist in the space, RCWs (Runtime-Callable Wrappers) and CCWs (COM-Callable Wrappers). RCWs are .NET assemblies act as the bridge between .NET and COM. Conversely, CCWs are COM components act as the bridge between COM and .NET.

To use Microsoft Agent in our Windows Forms application, simply, we’ll start by adding the Microsoft Agent Control to our toolbox. Figure 3 shows the Choose Toolbox Items dialog in the COM Components tab.

Figure 3 - Adding Microsoft Agent Control into Toolbox
Figure 3 – Adding Microsoft Agent Control into Toolbox

This component contains a single control Microsoft Agent Control that will be added automatically to the toolbox. After dragging it to the form, two components will be added automatically to the project references, AgentObjects and AxAgentObjects. These are the interoperability RCW wrapper components for the COM component. AxAgentObjects is the wrapper for the control and its related types. On the other hand, AgentObjects component is the wrapper for other types in the COM component. You might have noticed that a square-shaped control of type AxAgentObjects.AxAgent added to the form. This control will not be visible at runtime. Figure 4 shows the form.

Figure 4 - Microsoft Agent Control
Figure 4 – Microsoft Agent Control

AgentCtl uses STA (Single-Threaded Apartment) threading model. Means that the COM component supports -and bounds- only to a single thread.

To enable STA for your application you need to adorn the Main() function with the STAThreadAttribute attribute. Conversely, MTA (Multi-Threaded Apartment) threading model means that the COM component supports and can be accessed by multiple threads.

If neither STAThreadAttribute nor MTAThreadAttribute attributes has not been set, the threading model by default is MTA. Another way to set the threading model is the System.Threading.Thread.SetApartmentState instance (opposite of static) method.

To know whether a COM component supports STA, MTA, or both threading models, you can check the registry value ThreadingModel on HKCRCLSIDInprocServer32.

Loading Characters

After adding the AxAgent control to the form, you can now order the control to load the character(s) and show it.
AxAgent control contains a property Characters that’s of type AgentObjects.IAgentCtlCharacters that acts as a dictionary collection contains the loaded characters and its names.

For loading a character we use the Load() method of the Characters collection. For getting the loaded character we use the Character() method that returns an object of type AgentObjects.IAgentCtlCharacterEx that represents the loaded character.

Here’s the code for loading the character Merlin and showing it:

// 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);

In addition to Load() and Character() methods of the IAgentCtlCharacters object, you can use the Unload() method to unload a given loaded character.

Also IAgentCtlCharacterEx supports the Hide() method for hiding the character, that’s opposite to the Show() method.

Ordering the Character to Speark

After creating the character and showing it you can order it to speak whether a specific text using the Text-to-Speech (TTS) Engine or a recorded audio file. The following code segment makes the character introduces itself using coded string first and a recorded audio file next:

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

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

As you have noticed, the Speak() method can take either a text for the first argument or a file path for the second argument. Figure 5 shows Merlin while speaking the text.

Figure 5 - Merlin Speaks
Figure 5 – Merlin Speaks

Take a look at the Think() method, it is very similar to Speak().

Moving the character

You can move the character around the screen by using the MoveTo() method.

character.MoveTo(100, 100, null);

This method takes three arguments, the first and second argument is the location on the screen that you wish to move the character to. The third argument takes a value indicating the moving speed. If this argument is null, then the default value 1000 is used. If you specified the value 0 the character will move without playing an animation. Obviously, a value under 1000 will slow down the character move, and a value greater than 1000 will speed up the move.

I had reasons to merge this section with the animation section. But, I had reasons too  to put it in a single section, I think it’s a special type of animations!

Character Animation

Every character has its own animations. Although, many characters share many animation names but that’s not required.

Every animation has a name. And to get the animation names that a character supports, you need to dig into the AnimationNames property that’s of type AgentObjects.IAgentCtlAnimationNames which implements the System.Collections.IEnumerable interface.

The following code enumerates the animation names of the character:

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

Now, it’s time to have some fun with the character. Try the following code:

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

Figure 6 shows Merlin speaking while doing some magic. (Proud Merlin)

Fogure 6 - Merlin Does Magic
Fogure 6 – Merlin Does Magic

Be aware that animation names that contain directions like left and right, it’s the left and right of the character not you.

In addition to the Play() method, the character object supports two methods for stopping playing animations, Stop() and StopAll(). Stop() stops the animation that’s specified by its single argument. StopAll() stops all animation of a specific type. This method has a single argument that can takes a string value of three “Move”, “Play”, and “Speak” to stop moving, animation or speaking commands. In addition to the three values, StopAll() can take a null value, and that means stopping all types of animations.

You might have noticed that many methods of the character object returns an object of type AgentObjects.IAgentCtlRequest. This type encapsulates a request to the character. If IAgentCtlRequest.Status is non-zero then the operation failed, otherwise succeeded.

Pay attention to the methods that return IAgentCtlRequest object, and the methods that requires it as an argument like Stop() method.

Another type of animation is gesturing to a specific point. Try this animation using the GestureAt() method.

IAgentCtlCharacterEx Properties

This list contains the most common properties of the character object (IAgentCtlCharacterEx):

  • AutoPopupMenu:
    A Boolean property indicates whether to allow the character pop-up menu or not. Pop-up menu is the menu that is shown when the user right-clicks on the character or its icon -if exists- on the taskbar. Default value is False.
  • Balloon:
    This is a read-only property that’s in turn contains read-only properties that indicate the characteristics like the back color of the balloon displayed while the character talking. This property contains many properties that most are self-explained from its names.
  • Left and Top:
    Controls the character location on the screen. (It is better using the MoveTo() method instead of these two properties if you want to play an animation while moving the character).
  • MoveCause:
    A read-only property returns a value indicates what caused the character’s last move. This property can return one of 5 values:

    • 0:
      The character has not been moved.
    • 1:
      The user moved the character.
    • 2:
      Your application moved the character.
    • 3:
      Another client application moved the character.
    • 4:
      The Agent server moved the character to keep it onscreen after a screen resolution change.
  • Pitch:
    A read-only property returns a value indicates the Pitch setting of the TTS.
  • SoundEffectsOn:
    Set this property to True to enable the sound effects, of False to disable it. Default is True.
  • Speed:
    A read-only property indicates the speed of the character. (Also there’re some operations that supports specifying the speed like the MoveTo() method).
  • VisibilityCause:
    A read-only property returns a value indicates the character’s visibility state. This property can return one of 8 values:

    • 0:
      The character has not been shown.
    • 1:
      User hid the character using the command on the character’s taskbar icon pop-up menu or using speech input..
    • 2:
      The user showed the character.
    • 3:
      Your application hid the character.
    • 4:
      Your application showed the character.
    • 5:
      Another client application hid the character.
    • 6:
      Another client application showed the character.
    • 7:
      The user hid the character using the command on the character’s pop-up menu.

Agent Server is the hard-core component that controls the Microsoft Agent behind the scenes. It controls all the characters loaded by your application and other applications and Websites. It’s the one which servers all orders for every Microsoft Agent character. Worth mentioning, it’s contained in the executable file AgentSvr.exe located on %windir%MSAgent. This file will be launched automatically when the first character loaded, and will shuts down automatically when the last character unloaded. After all, you can think about the Agent Server as the hard-core internal component of the Microsoft Agent.

AxAgent Control Events

An odd behavior of that control is that it defines events that related to the character and most likely to be located in the character object itself. Maybe it’s a feature to be located here because AxAgent controls the birth and death of the characters, but most of the time you will find that annoying.

The most widely-used events are:

  • BalloonShow and BalloonHide:
    Occur when a balloon of a loaded character shown or hid.
  • ClickEvent and DblClick:
    Occur when a loaded character clicked or double-clicked.
  • DragStart and DragComplete:
    Occur when a user tries to drag a loaded character.
  • ShowEvent and HideEvent:
    Occur when a loaded character shown or hide, whether the user who made this or your code. If you want to check what caused the character to be shown check the IAgentCtlCharacterEx.VisibilityCause. As a refresher, read the last section to know about this property.
  • MoveEvent:
    Occurs when a loaded character moved whether the user moved it or it’s moved by your code. See the IAgentCtlCharacterEx.MoveCause in the previous section if you want to know what caused the character to be moved.
  • RequestStart and RequestComplete:
    Occur when a request being restarted or after it completed. Note that many methods accept and many others return IAgentCtlRequest objects that defines requests sent to the server.

Creating a Custom Popup Menu

After you have included your desired agent in your application, are you feeling bad with the default popup menu? If so, then you are in the right place.

First, create your System.Windows.Forms.ContextMenuStrip and add your required items (well, including “Hide” maybe) and complete the item event handlers.

Now, let’s get it. Get to the code that loads the agent character (e.g. calls the Characters.Load() method of the agent control object, AxAgentObjects.AxAgent) and just disable the AutoPopupMenu flag/property of the character object, AgentObjects.IAgentCtlCharacterEx. This flag/property determines whether to allow the default popup menu or not.

For example, the following code disables this property:

AxAgentObjects.AxAgent agentCtl;
AgentObjects.IAgentCtlCharacterEx agentChar;

// initializing 'agentCtl'
// . . .

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

Next comes the interesting point. When the character is clicked, the ClickEvent event of the agent control (AxAgent) fires. So the next step is to handle this event and to provide your code that brings up your custom context menu. Consider the following code:

// 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);
        }
    }
}

Creating a Microsoft Agent controller

When dealing with Microsoft Agent, there’s recommended practices. One of is abstracting the Microsoft Agent implementation code from the UI code for achieving the maintainability and usability goals, and these goals can be achieved through a controller class that encapsulates the functionality that you need and provides easy access to lots of operations using a single call. A good controller class example is like this:

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");
    }
}

Freeing up Resources

After finishing your work with Microsoft Agent you should first unload the character by calling AxAgent.Characters.Unload() method. Second, because all objects of the RCW (Runtime-Callable Wrapper) assemblies created earlier are just a wrapper around the unmanaged COM objects, they exhaust a great amount of unmanaged resources that can retain in memory for a long time, you strictly should always dispose these object when you have done with it.

Fortunately, AxAgent control inherits indirectly from the IDisposable interface, so you can easily dispose it by calling its Dispose() method. Conversely, most objects (like the character object IAgentCtlCharacterEx) in the RCWs do not implement this interface, so you need to do it the hard way and use the Marshal class to dispose it. Here’s the code that can do that:

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

The FinalReleaseComObject() simple tries to decrement the references of the COM object to 0. You need to call FinalReleaseComObject() again and again until it returns 0, means that no references remain.

FinalReleaseComObject() tries to decrement the reference count to 0 in one time, ReleaseComObject() tries to decrement it gradually. So ReleaseComObject() needs more calls than FinalReleaseComObject(). and FinalReleaseComObject() is faster than ReleaseComObject().

Decrementing object’s references means disposing it.

You learned how to free-up the resources. But, there’s a question now. At which point should you dispose objects? It depends! The point at which you need to dispose objects depends on your use of objects. First, if you finished working with the AxAgent control and you do not want it any more till the user closes your application, it’s recommended that you dispose it as soon as possible. Otherwise, add the Dispose() call to the protected Dispose(bool) method of the form, so that it disposed when the form closes (honestly, disposes). The following code snippet demonstrates this:

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

This code strictly follows the disposing design guidelines.

You can step to the Dispose() method from the Members combo box in the top-right of the Code Editor window of the form.

A programming extremist like me (one of the Qaeda squad programming team :P) always disposes his objects even if the application is shutting down. To be honest, when the application shuts down all resources and memory managed by the application will be released immediately, so you actually do not need to release objects while the application shuts down.

On the other hand, for some objects like IAgentCtlCharacterEx you should take the Marshal class approach. But where to use it?

A great location for the FinalReleaseComObject() method (or ReleaseComObject() as well) is in the controller class. You can make your controller class implements the IDisposable interface and add your code inside the Dispose() method. Check the following example:

Code abbreviated for clarity.

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) ;
    }
}

You should dispose the controller class as soon as you finish using your character, or even with the form disposal along with the disposing of the AxAgent control.

The Last Word

Needless to say that you can take advantage of the Microsoft Agent and TTS technology to produce applications that provide friendly tutor for the user.

You are not end up using existing Microsoft Agent characters, you can create your own characters and animations using the Microsoft Agent Character Editor that’s available for download in the Microsoft Agent SDK download page.

Microsoft Agent Compatibility

Microsoft announced that Windows 7 would not be compatible with Microsoft Agent. However, you still can install the SDK and run your applications correctly.

Where to Go Next?

Good references for Microsoft Agent are:

  • Microsoft Agent SDK Documentation. Available for download with the SDK, see “Micrososft Agent SDK” section on the top.
  • Book: Microsoft Agent Software Development Kit.
    ISBN: 0-7356-0567-X
  • Book: Developing for Microsoft Agent.
    ISBN: 1-5723-1720-5

A Real-world Application

This lesson does not contain a sample project, it contains a real-world application, Geming PartIt! that’s used for parting large files into small pieces for merging them later, so you can send them via e-mail or storing them on CDs and Floppy Disks. This application uses Microsoft Agent character Merlin to provide a friendly guide for the user through the application.

Download the sample app!
Download the sample source!


It is my pleasure receiving comments and feedbacks about my application (and code of course.)