Creating an Office 2003 Research Service Using the Amazon Web Service API

 

Frank Rice
Microsoft Corporation

Charles Maxson
OfficeZealot.com

December 2004

Applies to:
    Microsoft Office Excel 2003
    Microsoft Office Outlook 2003
    Microsoft Office Word 2003

Summary: Search for books at Amazon.com using the Amazon.com research service, then include the results returned in your sheet or document, without leaving your Office 2003 application. The sample solution shows you how to implement a custom research service. (34 printed pages)

Download OfficeCreateAnAmazonResearchServiceSample.exe.

Download the OfficeCreateAnAmazonResearchServiceSample.exe sample file. (897 KB)

Contents

What Is a Custom Research Service?
The Advantages of the Amazon.com Research Service
How Do You Build a Custom Research Service?
Processes and Components of the Amazon.com Research Service
Exploring the Registration Web Method
Exploring the Query Web Method
Using Smart Tags with the Amazon.com Research Service
Installing and Setting Up the Amazon.com Research Service
Conclusion
Additional Resources

What Is a Custom Research Service?

The Microsoft Office 2003 Research services feature provides a way to quickly locate and use the information that you need without leaving the Office application that you are working in. The Research services feature provides a powerful and integrated search experience. This functionality gives you the ability to search multiple custom and third-party references and the convenience of accessing this information from within Office 2003 applications. You can:

  • Copy text from search results to an application
  • Insert selected text from search results to an application
  • Look up subsequent terms from search results
  • Link to related Web pages
  • Filter results
  • Use smart tag actions

The Research service feature is available in Microsoft Office Word 2003, Microsoft Office Excel 2003, Microsoft Office Outlook 2003, Microsoft Office OneNote 2003, Microsoft Office PowerPoint 2003, Microsoft Office Publisher 2003, Microsoft Office Visio 2003, and Microsoft Internet Explorer. The Research service feature interface tightly integrates with these Office 2003 applications so that it can extract information from the Office application or insert content into it.

In this article, we examine the custom research service for the book retailer, Amazon.com.

The Advantages of the Amazon.com Research Service

The Amazon.com solution illustrates the concept of the Research services feature by offering branded content from Amazon's catalog of books, enabling you to find and examine detailed information about books from within the current working environment of an Office document. Figure 1 shows a sample of the search results in the research task pane.

Figure 1. Search results from in the research task pane

The research task pane also offers easy access to common Office informational resources such as dictionary and thesaurus. Searching for information can be done by typing in a word in the Search For: field, or simply clicking a word in the document while holding down the Alt key. The research task pane also offers an open API that enables developers to offer searchable contextual data directly to users working with Office 2003 documents.

How Do You Build a Custom Research Service?

The research service primarily consists of a Web service with two methods. In addition, the research service is a standard Web service and requires no client-side code (unless you plan to use an optional smart tag).

A Web service is an application available on the Web that other applications can share, regardless of the platform and the language that the applications use. Generally, a Web service performs some common task such as querying a data source and providing the results of the search. Other application developers can subscribe to the Web service without having to implement the code themselves. This is made possible by the open standards that Web services support such as XML, SOAP, Web Service Description Language (WSDL), and Universal Description, Discovery, and Integration (UDDI).

Research services are implemented as Web services. A provider offering a research service provides a URL to the Web service. You can subscribe to the Web service by specifying the URL when adding the research service through the research task pane.

Processes and Components of the Amazon.com Research Service

The architecture of research services is relatively straightforward.

Figure 2. Architecture of the Amazon.com research service

Typically, you build a custom research service or set of services as a Web service or pair of Web services. This Web service corresponds to a "provider" such as Amazon.com, and can process various types of searches, each of which corresponds to a "service." In some cases, as with this sample, an intermediate Web service is constructed to provide other services such as string formatting and schema adherence.

All communication between the research task pane and the Web services uses SOAP messages. These SOAP messages contain XML packets that conform to schemas specific to each type of request and response.

To develop a custom research service, you must write code to respond to the two scenarios in which Office 2003 clients contact the service: when the user registers the service for the first time (or updates it later), and when the user queries the service. Therefore, the Web service normally contains the Registration and Query Web methods that respond to these two events. In both cases, the research task pane sends a properly formatted request packet to the Web service, and the Web service responds with a response packet that conforms to the appropriate schemas.

Exploring the Registration Web Method

As mentioned previously, a research service typically consists of a Web service with two methods. These methods can exist in one Web service file but do not have to do so. In the Amazon.com solution described here, the two key files exist as:

  • The Registration.asmx Web service file containing the Registration Web method
  • The Query.asmx Web service file containing the Query Web method

Before executing queries against the research service, the client Office application needs to know certain information about the service and that the service exists. This process is known as registration. Each research service that is available on a client computer is defined by a group of registry settings. Therefore, installing or deploying a research service involves adding those registry settings to the client computer. The Registration Web method of a research service sends the values for those settings to the client computer when the user chooses to add the service by typing in the URL, or by selecting an advertised service. The research task pane creates the registry settings based on the registration response. One such list of possible research providers available to end users is shown in Figure 3.

Figure 3. List of research service providers

The Amazon.com Registration Web method receives an incoming string value from the research task pane, in the form of an XML document, and returns a string value describing the provider and its services, in the form of an XML document derived from the RegistrationResponse.xml file.

The following code listing illustrates the procedures used in the Registration Web method:

 [WebMethod(CacheDuration=86400)] //Cache this for a full day (86400 seconds = 24 Hours)
public string Registration(string registrationxml)
{
string physicalPath = HttpContext.Current.Server.MapPath(".").ToString();
string httpPath = ConfigurationSettings.AppSettings["ServerPath"]  
+ HttpContext.Current.Request.ApplicationPath + "/";
    //Load the Registration reponse data.
XmlDocument registrationResponse = new XmlDocument();
registrationResponse.Load(physicalPath + "\\RegistrationResponse.xml");

XmlNamespaceManager nsm = new XmlNamespaceManager(registrationResponse.NameTable);
nsm.AddNamespace("ns", "urn:Microsoft.Search.Registration.Response");

registrationResponse.SelectSingleNode("//ns:QueryPath", nsm).InnerText = 
httpPath + "Query.asmx";
registrationResponse.SelectSingleNode("//ns:RegistrationPath", nsm).InnerText =
httpPath + "Registration.asmx";
registrationResponse.SelectSingleNode("//ns:AboutPath", nsm).InnerText =
httpPath + registrationResponse.SelectSingleNode("//ns:AboutPath", nsm).InnerText;

return registrationResponse.InnerXml.ToString();
}

The registration response is simple in that it only defines information about the research service. As stated earlier, the code loads a local XML file named RegistrationResponse.xml that functions as the overall template for the response. This information is returned to Office. The Amazon.com XML response data contained in the file RegistrationResponse.xml is shown below:

<?xml version="1.0" encoding="utf-8"?>
<ProviderUpdate xmlns="urn:Microsoft.Search.Registration.Response">
    <Status>SUCCESS</Status>
    <Providers>
        <Provider>
            <Message>Amazon.com offers the Earth's Biggest Selection of 
                products. With this demonstration application, you to 
                explore the products of Amazon  from the comfort of 
                Microsoft Office.</Message>
            <Id>{57EECFA5-4314-40af-827B-9688A0579306}</Id>
            <Name>Amazon.com</Name>
            <QueryPath/>
            <RegistrationPath/>
            <AboutPath>AboutService.aspx</AboutPath>
            <Type>SOAP</Type>
            <Services>
                <Service>
                    <Id>{B333CC52-55FB-4f26-9B55-3CAA6AB21FFA}</Id>
                    <Name>Books on Amazon.com</Name>
                    <Description>Welcome to the Amazon Product Catalog 
                        Search Utility for Microsoft Office 2003. This 
                        demonstration application will allow you to 
                        explore the products of Amazon from the comfort 
                        of Microsoft Office.
                    </Description>
                    <Copyright>
                        Amazon.com, Inc. Copyright (c) 2003.
                    </Copyright>
                    <Display>On</Display>
                    <Category>ECOMMERCE_GENERAL</Category>
                </Service>
            </Services>
        </Provider>
    </Providers>
</ProviderUpdate>

After successful registration, the data can be found in the following location:

HKEY_CURRENT_USER\Software\Microsoft\Office\11.0\Common\Research\Sources

This key in the registry contains a list of the various research providers to which an Office application can be connected. Figure 4 shows the data from the registry entry for the Amazon.com research service provider:

Figure 4. Registry listing for the Amazon.com research service provider

Once registered, the service is available as a choice in the show results drop-down list that is located just below the Search for text box. When the user installs the research service as a task pane resource, a request is sent to the Registration Web method. The response received is used to make appropriate entries in the registry.

Office expects the Registration Web method and the following Query Web method to respond according to the research service and their associated namespace. Therefore, you must define each Web service with the appropriate namespace for a response, which is urn:Microsoft.Search. The following attribute for the Web service is:

[WebService(Namespace="urn:Microsoft.Search")]

Exploring the Query Web Method

The next component of the Web service is the Query Web method. The Query Web method is central to a research service. The research task pane submits the user's query to the Query Web method, along with information about the research task pane, the Office application, and other user settings. The Query Web method responds with the query results, which may consist of simple hyperlinks or richly formatted content, and may be enhanced with form controls and actions. Figure 5 illustrates the steps in this process.

Figure 5. The processes of the Query Web method

When a user makes a request for information through the research task pane, the request is packaged up according to the schema for the request. This information is then passed to the Query Web method and is available for parsing in the queryXml parameter. Query extracts the search term from queryXml and then passes the term to the QueryAmazonBooksAuthor function. The AmazonQueryAuthor function is then called to retrieve the actual data from the Amazon.com research service. This data is returned to QueryAmazonBooksAuthor where it is parsed and repackaged according to the research service response schema.

The following pseudo-code provides an overall description of the workings of the Query Web method. It also indicates calls between the various methods:

[WebService(Namespace="urn:Microsoft.Search")]
public class QueryService: System.Web.Services.WebService
//Create the container for the Web method

[WebMethod(CacheDuration=86400)]  //Cache for 1 day
public string Query(string queryXml)

// This code is triggered in response to a query request. This code
// performs a search of the Amazon.com data and populates a response 
// packet which is then encapsulated in a SOAP wrapper for transport
// back to the calling procedure, in this case, the Office application.

  Load XML query into a new XML Document
  Determine the search type by first testing
  if the query string starts with the string PreAsin
    Replace PreAsin with empty string
    Reload query text with new string
    Set the search type to Asin
  Else
    If query string starts with string PreAuthor
      Replace PreAuthor with empty string
      Reload query text with new string
      Set the search type to Author
    Else
      Set the default search type to Keyword
  Get and set the start page number for the query
  Load ResponsePacketWrapper.xml into a new XML document
  Populate the response wrapper by calling the search procedure
    based on the search type.

private string QueryAmazonBooksAuthor(string queryString, int startAt)

// Just as with the previous procedure, this procedure is called from 
// the Query module. The module retrieves the search data and builds 
// the response package which is then passed back to the calling 
// procedure where it is then encapsulated in a wrapper for transport.

  Fetch Amazon data into a new XML document by calling AmazonQueryAuthor.
  Create a stringwriter to build and write out the response.
  Build elements for the Previous | Next links
  Build the response by creating the elements that
    make up the response screen such as logo, separator lines, etc.
    Also, format the response by book(s); titles, heading, author, 
      price, ratings, etc.
  Returns a queryResponse object 

private string QueryAmazonBooksAsin(string queryString)

// This procedure is called from the Query module and performs 
// essentially the same function as QueryAmazonBooksAuthor with the
// addition of the smart tag statements.

  Fetch Amazon data into XML document by calling AmazonQueryAsin
  Create a stringwriter to build and write out the response.
  Build the response by creating the elements that
    make up the response screen such as logo, separator lines, etc.
    Also, format the response by book(s); titles, heading, author, 
      price, ratings, etc.
  Add in the appropriate star rating graphic by calling StarGif
  Build and pass actions and content to the smart tag in the calling
    application.
  Returns a queryResponse object

private string QueryAmazonBooksKeyword(string queryString, int startAt)

//Same function as QueryAmazonBooksAuthor.

...
  Returns a queryResponse object

private XmlDocument AmazonQueryAsin(string queryString, string sType)

// Returns an XML document resulting from a search of an Amazon product 
// catalog by Asin

    Create a new XML document
    Load an AmazonDevToken
    Load formatted XML string into XML document

private XmlDocument AmazonQueryKeyword(string queryString, string sMode, 
  string sType, int iPage)

// Returns an XML document resulting from a keyword search of the 
// Amazon product catalog by keyword.

    Create a new XML document
    Load an AmazonDevToken
    Remove %20's (spaces) from query string by calling SpaceString
    Load formatted XML string into XML document

private XmlDocument AmazonQueryAuthor(string queryString, string sMode, 
  string sType, int iPage)

// Returns an XML document resulting from a search of the Amazon product 
// catalog by author.

    Create a new XML document
    Load an AmazonDevToken
    Remove %20's (spaces) from query string by calling SpaceString
    Load formatted XML string into XML document

private string SpaceString(string searchString)

// Replaces %20 characters (URL blank space eqivalent) with blank spaces.

public String StarGif (bool bWhole, double dValue)

// Returns a graphic based on the value of a rating score.

The following listing is a segment of the code for the Query Web method and one of its dependent functions (QueryAmazonBooksAuthor):

[WebMethod(CacheDuration=86400)]  //Cache for 1 day
public string Query(string queryXml)
{
    //Verify that a query value has been specified
    if (queryXml.Length == 0)
        return "";

    string queryString;
    int startAt;

    XmlDocument requestXml = new XmlDocument();
    XmlNamespaceManager nsmRequest = new XmlNamespaceManager(requestXml.NameTable);

    try
    {
        requestXml.LoadXml(queryXml.ToString());

        nsmRequest = new XmlNamespaceManager(requestXml.NameTable);
        nsmRequest.AddNamespace("ns", "urn:Microsoft.Search.Query");
        nsmRequest.AddNamespace("oc", "urn:Microsoft.Search.Query.Office.Context");

        queryString = requestXml.SelectSingleNode("//ns:QueryText", nsmRequest).InnerText;
        applicationName = requestXml.SelectSingleNode("//oc:Name", nsmRequest).InnerText;

        if (queryString.StartsWith(preAsin))
        {
            queryString = queryString.Replace(preAsin, "");
            requestXml.SelectSingleNode("//ns:QueryText", 
                nsmRequest).InnerText = queryString;
            requestXml.SelectSingleNode("//ns:Requery", 
                nsmRequest).InnerText = queryString;
            SearchType = SearchTypes.Asin;
            else
        {
            if (queryString.StartsWith(preAuthor))
            {
                queryString = queryString.Replace(preAuthor, "");
                    requestXml.SelectSingleNode("//ns:QueryText", 
                    nsmRequest).InnerText = queryString;
                requestXml.SelectSingleNode("//ns:Requery", 
                    nsmRequest).InnerText = queryString;
                SearchType = SearchTypes.Author;
            }

            else
            {
                    SearchType = SearchTypes.KeyWord;
            }
        }
    }
    catch
    {
        //Parsing queryXML has failed, 
        //use the input string from the search - run as keyword search
        //Call may have come from non-Office application e.g. IE
        queryString = queryXml;
    }

Looking at the code, after verifying that a valid query has been received, a name table for the XmlNamespaceManager object is created. A XmlNamespaceManager object stores namespace prefixes and namespaces themselves as strings. A NameTable object is used to look up these prefixes and namespace URIs for use in other operations. At the beginning of the Try...Catch block, the data from the incoming query is loaded. Then the various namespaces are loaded into the namespace manager. Next, the search type of the query is determined. If it is determined that no search type can be parsed from the query, the default keyword search is used.

    //Get start page number
    try
    {
        startAt = Convert.ToInt32(requestXml.SelectSingleNode("//ns:StartAt", 
    nsmRequest).InnerText);
    }
    catch
    {
        startAt = 1;
    }

    //Load response body wrapper XML
    XmlDocument responseWrapper = new XmlDocument();
    try
    {
        string physicalPath = HttpContext.Current.Server.MapPath(".").ToString();
        responseWrapper.Load(physicalPath + "\\ResponsePacketWrapper.xml");
    }
    catch 
    {    //If unable to load the response body wrapper file, 
    return zero length string
        return "";
    }
    XmlNamespaceManager nsmResponse = new 
        XmlNamespaceManager(responseWrapper.NameTable);
    nsmResponse.AddNamespace("ns", "urn:Microsoft.Search.Response");

    switch (SearchType) 
    {
        case SearchTypes.Asin:
            responseWrapper.SelectSingleNode("//ns:Results",nsmResponse).InnerXml 
        = QueryAmazonBooksAsin(queryString);

            break;

        case SearchTypes.KeyWord:
            responseWrapper.SelectSingleNode("//ns:Range",nsmResponse).InnerXml 
        = QueryAmazonBooksKeyword(queryString, startAt);
  
            break;

        case SearchTypes.Author:
            responseWrapper.SelectSingleNode("//ns:Range",nsmResponse).InnerXml = 
        QueryAmazonBooksAuthor(queryString, startAt);

            break;

        default:

            break;
    }

    return responseWrapper.InnerXml.ToString();

}

Continuing our examination, the starting page for the search is next determined, with the default being the first page. Next, the query response body wrapper is loaded from the file ResponsePacketWrapper.xml. This is done by mapping the virtual path in the requested URL to the physical path of the wrapper on the server. Next, one of three dependant functions is called based on the value of the search type. The code then places the search results into the response wrapper.

private string QueryAmazonBooksAuthor(string queryString, int startAt)
{

    int iPage = Convert.ToInt32((startAt/10) +1);
    
    //Fetch Amazon data into XML document
    XmlDocument xmlResult = new XmlDocument();
    xmlResult = AmazonQueryAuthor(queryString,"books", 
        bUseLiteSchema ? "lite" : "heavy", iPage);

    XmlNamespaceManager nsmRequest = new 
    XmlNamespaceManager(xmlResult.NameTable);
    
    // The flag bUseLiteScheme, defined in the namespace, determines 
    // if the heavyor lite schema is used.
    nsmRequest.AddNamespace("xsi", bUseLiteSchema ?
        "http://xml.amazon.com/schemas2/dev-lite.xsd" :
        "http://xml.amazon.com/schemas2/dev-heavy.xsd");
        
    //Create stringwriter object to build out response
    StringWriter queryResponse = new StringWriter();
    XmlTextWriter writer = new XmlTextWriter(queryResponse);
    
    // Create an XmlNode for testing in code below.
    XmlNode xmlNode = null;            
    
    try
    {
        //Elements for Previous | Next links
        xmlNode = xmlResult.DocumentElement.SelectSingleNode
            ("//ProductInfo/TotalResults");
        int nTotal = 0;
        if (null != xmlNode)
        {
            nTotal = Convert.ToInt32(xmlNode.InnerText);
        }
        int nPage = Convert.ToInt32(ConfigurationSettings.
            AppSettings["ResultsPerPage"]);
        if (nTotal < nPage)
            {nPage = nTotal;}

        writer.WriteElementString("StartAt",startAt.ToString());
        writer.WriteElementString("Count",nPage.ToString());
        
        writer.WriteElementString
        ("TotalAvailable",nTotal.ToString());

        //Start Results element
        writer.WriteStartElement("Results");
    
        //Start Content element
        writer.WriteStartElement("Content", 
            "urn:Microsoft.Search.Response.Content");

        //Insert Logo
        writer.WriteStartElement("Image");
        writer.WriteAttributeString("source", httpPath + 
            "Images/AmazonFDB.bmp");
        writer.WriteEndElement();

        //Insert Author Line (text)
        writer.WriteStartElement("P");
        writer.WriteStartElement("Char");
        writer.WriteAttributeString("Font","arial");
        writer.WriteAttributeString("smallCaps","true");
        writer.WriteAttributeString("bold","true");
        writer.WriteString("Books by " + queryString + "...");
        writer.WriteEndElement();
        writer.WriteEndElement(); //Author Line 

        //Separator Line
        writer.WriteElementString("HorizontalRule", "");

        //Get result records
        XmlNodeList nodeList =
            xmlResult.DocumentElement.SelectNodes("/ProductInfo/Details");
    
        //Loop through search results and write each one to the 
        //XmlTextWriter.
        foreach(XmlNode item in nodeList)
        {
            WriteItem(item, writer);
        }
    }
    catch
    {
        WriteNoResultsFound(queryString, writer);
    }
    
    writer.WriteEndElement(); //Close Content element
    writer.WriteEndElement(); //Results Content element

    writer.Flush();
    writer.Close();

    return queryResponse.ToString();
}    

In the QueryAmazonBooksAuthor method, the search results are copied into an XML document. Then, in preparation of writing out the results as formatted XML, a StringWriter object is created. In the foreach block, the various elements of the formatted XML, including both data and XML labels, are created and writen to the XmlTextWriter via the WriteItem helper function.

// Write out the contents of a match into an XmlTextWriter
// with this helper function.
private void WriteItem(XmlNode item, XmlTextWriter writer)
{
XmlNode nodeToWrite = null;
    
    writer.WriteStartElement("Heading");
    writer.WriteAttributeString("collapsible","true");
    writer.WriteElementString("Text", 
    item.SelectSingleNode("ProductName").InnerText);
    
    //Authors
    XmlNodeList nodeListAuthor = item.SelectNodes("Authors/Author");
    writer.WriteStartElement("Line");
            
    //Insert Book logo
    writer.WriteStartElement("Image");
    writer.WriteAttributeString("source", 
        httpPath + "Images/small-blue-books-icon[1].gif");
    writer.WriteEndElement();    
        
    //Loop through Authors
    bool bFirstElement = true;
    foreach(XmlNode itemAuthor in nodeListAuthor)
    {
        if (bFirstElement) 
        {
            writer.WriteElementString("Char", "By");
            bFirstElement = false;                        
        }
        else
        {
            writer.WriteStartElement("Char");
            writer.WriteAttributeString("noPadding","true");
            writer.WriteString(",");
            writer.WriteEndElement(); //","
        }
                
        writer.WriteStartElement("NewQuery");
        writer.WriteAttributeString("query",preAuthor + 
            itemAuthor.InnerText);
        writer.WriteElementString("Text", itemAuthor.InnerText);
        writer.WriteEndElement(); //Author
        
    }
    writer.WriteEndElement(); //Authors
        
    //Book Details
    writer.WriteStartElement("P");

    //Manufacturer
    nodeToWrite = item.SelectSingleNode("Manufacturer");
    if (null != nodeToWrite)
    {
        writer.WriteStartElement("Char");
        writer.WriteAttributeString("italic", "true");
        writer.WriteString(nodeToWrite.InnerText);
        writer.WriteEndElement(); //Manufacturer
    }                
        
    //ReleaseDate
    nodeToWrite = item.SelectSingleNode("ReleaseDate");
    if (null != nodeToWrite)
    {
        writer.WriteString(" (" + nodeToWrite.InnerText + ")");
    }
        
    writer.WriteEndElement();//Book Details

    //Book Cover
    nodeToWrite = item.SelectSingleNode("ImageUrlSmall");
    if (null != nodeToWrite)
    {
        writer.WriteStartElement("Image");
        writer.WriteAttributeString
            ("source",nodeToWrite.InnerText);
        writer.WriteEndElement(); //Book Cover
    }
        
    //Prices 
    //ListPrice
    nodeToWrite = item.SelectSingleNode("ListPrice");
    if (null != nodeToWrite)
    {
        writer.WriteStartElement("Line");
        writer.WriteStartElement("Char");
        writer.WriteAttributeString("bold", "true");
        writer.WriteString("List Price:");
        writer.WriteEndElement(); //List Price:
        writer.WriteStartElement("Char");
        writer.WriteAttributeString("bold", "false");
        writer.WriteString(nodeToWrite.InnerText);
        writer.WriteEndElement(); //Value
        writer.WriteEndElement(); //ListPrice
    }

    //OurPrice
    nodeToWrite = item.SelectSingleNode("OurPrice");
    if (null != nodeToWrite)
    {
        writer.WriteStartElement("Line");
        writer.WriteStartElement("Char");
        writer.WriteAttributeString("bold", "true");
        writer.WriteString("Our Price:");
        writer.WriteEndElement(); //List Price:
        writer.WriteStartElement("Char");
        writer.WriteAttributeString("bold", "false");
        writer.WriteString(nodeToWrite.InnerText);
        writer.WriteEndElement(); //Value
        writer.WriteEndElement(); //OurPrice
    }
        
    //UsedPrice
    nodeToWrite = item.SelectSingleNode("UsedPrice");
    if (null != nodeToWrite)
    {
        writer.WriteStartElement("Line");
        writer.WriteStartElement("Char");
        writer.WriteAttributeString("bold", "true");
        writer.WriteString("Used & New");
        writer.WriteEndElement(); //Used & New"
        writer.WriteStartElement("Char");
        writer.WriteString("from");
        writer.WriteEndElement(); //from
        writer.WriteStartElement("Char");
        writer.WriteAttributeString("bold", "false");
        writer.WriteString(nodeToWrite.InnerText);
        writer.WriteEndElement(); //Value
        writer.WriteEndElement(); //UsedPrice
    }
        
    //See More Details (Asin Query)
    writer.WriteStartElement("NewQuery");
    writer.WriteAttributeString("tooltip",item.
    SelectSingleNode("ProductName").InnerText);
    writer.WriteAttributeString("query", preAsin + 
    item.SelectSingleNode("Asin").InnerText);
    writer.WriteElementString("Text", "See More Details");
    writer.WriteEndElement(); //See More Details
    
    writer.WriteEndElement(); //Heading

    //Separator Line
    writer.WriteElementString("HorizontalRule", "");        
}


//Returns an XMLDocument resulting in a search of an 
//Amazon book catalog by Author
private XmlDocument AmazonQueryAuthor
    (string queryString, string sMode, string sType, int iPage)
{
    XmlDocument xmlResult = new XmlDocument();

    string developerToken=ConfigurationSettings.AppSettings["AmazonDevToken"];
    queryString = SpaceString(queryString);

    xmlResult.Load ("http://xml.amazon.com/onca/xml2?t=webservices-20" 
        + "&dev-t="    + developerToken  
        + "&AuthorSearch=" + queryString 
        + "&mode=" + sMode
        + "&type=" + sType
        + "&page=" + iPage
        + "&f=xml");

    return xmlResult;
}

The WriteItem helper function inserts the data from a match into an XmlTextWriter. It takes two parameters, an XmlNode that the query created, and the XmlTextWriter. This method is called for each match produced by the query. Notice that it is good error handling practice to verify that an XmlNode returned by SelectSingleNode is not null before acting on it.

To finish off our discussion of the code, the AmazonQueryAuthor method returns an XML document resulting from a search by author of an Amazon book catalog.

Generally speaking, the Query Web method receives an incoming string value from the research task pane in the form of an XML document that conforms to the Microsoft.Search.Query family of schemas, and returns a string value containing the query results in the form of an XML document that conforms to the Microsoft.Search.Response family of schemas.

Throughout the code are instances that load the packet into an XmlDocument object and then use the SelectSingleNode method (and similar methods) to extract only the desired values.

The code for Query method also contains numerous references to the XMLTextWriter object. This object provides an efficient method of creating well-formed XML documents. It is important to present well-formed XML, otherwise the research task pane may show an empty pane in the search results. The following example demonstrates the use of the XMLTextWriter object:

StringWriter queryResponse = new StringWriter();
XmlTextWriter writer = new XmlTextWriter(queryResponse);
...
...
writer.WriteStartElement("P");
writer.WriteStartElement("Char");
writer.WriteAttributeString("Font","arial");
writer.WriteAttributeString("bold","true");
writer.WriteString("The search for '" + queryString + "' 
    did not return any results from Amazon.com.");
writer.WriteEndElement();

Which creates the following XML:

<P><Char Font="arial" Bold="True">The search for '" + queryString + "' 
    did not return any results from Amazon.com.</Char></P>

Using Smart Tags with the Amazon.com Research Service

The Amazon.com research service uses a client-side smart tag to provide a richer set of features to interact with documents. If the smart tag is not installed, the research service continues to function normally, with the exception that the smart tag menu items are not available.

When the Web service receives a query request, a search is performed and the results are packaged up into a query response. Included in that response is data to be used by the smart tag. Once the response XML data is received by the Office application, it caches the data, including the smart tag data, for later use.

Assuming that smart tags are enabled in the Office application and the user elects to view detailed information about the book, smart tag options are made available. When the user selects one of the smart tag actions, a verb representing that action and the cached XML data are sent to a built-in Office function where they are used as arguments for the InvokeVerb2 method. The verb is tested in a Select...Case statement and, depending on its value, the appropriate action involving the XML data is performed.

Figure 6 illustrates the Amazon.com process whereby a selection made by the user from the list of smart tag actions is translated into an action in the InvokeVerb2 function.

Figure 6. The Amazon.com smart tag process

The following segment of code is from the InvokeVerb2 method in the AmazonResearch.vbp file. In this segment, when the user makes the Insert Book Cover selection from the list of smart tag actions, the code executes a Select...Case statement to insert the book cover into the document.

Case 3 'Insert Cover Image
If Target Is Nothing Then
MsgBox "A document is required to use this action.", vbOKOnly, "Document Required"
Exit Sub
End If

Select Case ApplicationName
Case "Word.Application.11"
Set wdRange = Target
With wdRange.Application
.System.Cursor = wdCursorWait
.Selection.InlineShapes.AddPicture FileName:=sImageUrlMedium, 
    LinkToFile:=False, SaveWithDocument:=True
.System.Cursor = wdCursorNormal
End With

Case "Excel.Application.11"
Set xlRange = Target.Resize(1, 1)

Dim picCoverInsert As Excel.Picture
Set picCoverInsert = xlRange.Worksheet.Pictures.Insert(sImageUrlMedium)
xlRange.Worksheet.Hyperlinks.Add Anchor:=picCoverInsert.ShapeRange.Item(1), 
    Address:=sURL, ScreenTip:="Look '" & sTitle & "' up on amazon.com"

Case Else
MsgBox "This action is only supported in Microsoft Word and Excel.", vbOKOnly, 
    "Application Support Summary"

End Select

Installing and Setting Up the Amazon.com Research Service

Following are the requirements and procedures for installing and setting up the Web service for the Amazon.com research service on a client computer.

System Requirements for the Amazon.com Research Service

The Amazon.com Web service application for research services has the following software requirements:

  • Microsoft Office 2003 Editions
  • Microsoft Windows XP Professional or Microsoft Windows 2000 Server with Service Pack 2 or Microsoft Windows 2000 Professional with Service Pack (SP) 2
  • Microsoft .NET Framework 1.0
  • Microsoft Internet Information Services 5.0 or later
  • Optional: Microsoft Visual Studio .NET 2003 to edit the research pane Web services.
  • Optional: Microsoft Visual Basic 6.0 Professional with SP 5. The runtime engine is required for the smart tag dynamic linked library (DLL) or to edit the smart tag source code.

Installing the Amazon.com Research Service Solution

Copy the download that accompanies this article to your root directory (for example, C:\).

Follow the directions for your operating system for extracting the files from this download. For example, for Windows 2000 do the following:

  1. Click on the download file.
  2. Right-click and point to WinZip.
  3. Click Extract to.
  4. In the next screen, in the Extract to text box, type the root directory (C:\).
  5. Click Extract.

Configuring the Amazon.com Research Service Solution

To install the sample solution, follow the next three steps as outlined in the following set of procedures:

  1. Install the .NET&#8211based; Web service
  2. Install and register a client-side DLL file that provides the smart tag functionality in the research task pane
  3. Point Office 2003 Editions to the newly installed research task pane by registering the research service

Configuring the Amazon.com Web Service

To install the research service, follow these steps:

  1. Check to make sure that the default Web site in IIS is running. To do so:

    1. In Control Panel, open Administrative Tools, and then open Internet Services Manager (for Windows 2000) or Internet Information Services (for Windows XP Professional), which displays the Internet Information Services console.
    2. In the tree view pane, expand the node containing the name of your Web server (expand the Web Sites node if you're using Windows XP Professional).
    3. Right-click the Default Web Site node, and click Start if it is enabled (if Start is disabled, IIS is running).
  2. Using the Internet Services Manager, create a new virtual directory named AmazonResearch. The directory path for this virtual directory should be set to local_drive\install_path\ResearchLibrary.

    For example, C:\Amazon\ResearchLibrary*.*

  3. If the .NET Framework is not installed, install the .NET Framework Redistributable or install Microsoft Visual Studio .NET. Visual Studio .NET includes the .NET Framework, which is required to host the research service.

  4. By default, the Amazon.com sample solution is configured to use the root of http://localhost/ as its server. This allows the demo to run on the computer upon which it is installed, but it is not accessible remotely. To change the server name so that you can use the service remotely, edit the Web.config file found in local_drive\install_path\ResearchLibrary. Edit the value attribute of the following XML element:

    <add key="ServerPath" value="http://localhost"/>
    

    Change the value attribute to the name of your server. For example:

    <add key="ServerPath" value="http://www.MyServerName.com"/>
    
  5. The Amazon.com research service uses publicly provided Web services directly from Amazon.com. In order to use this content from Amazon.com, you are required to apply for a developer's token to ensure that you adhere to their terms and conditions for usage. For more information about Amazon's Web service initiative, see Web Services at Amazon.com.

    **Note    **Amazon no longer issues developer tokens. Instead they issue subscription IDs that can be used in any place where a developer token is requested

    Once you obtain a token, add it to the Web.config file. Edit the value attribute of the following XML element:

    <add key="AmazonDevToken" value="Your token goes here"/>
    

    Which should yield something similar to the following:

    <add key="AmazonDevToken" value="abcKS81ZTSVxyz"/>
    
  6. If you use a firewall to gain access to the Internet, check with your organization's network administrator to see if there is any special firewall client software that needs to be installed or any special connectivity settings that need to be made.

  7. Next, test that the Web service is functioning correctly by manually running the task pane registration Web service. Open a Web browser and navigate to:

    http://localhost/AmazonResearch/Registration.asmx

  8. Next, click the Registration link. This brings you to the Registration Web method page.

  9. Click the Invoke button. At this point, you should see a XML response packet from the Web service similar to the following:

    <?xml version="1.0" encoding="utf-8" ?> 
      <string xmlns="urn:Microsoft.Search"><?xml version="1.0" encoding="utf-8"?>
    <ProviderUpdate xmlns="urn:Microsoft.Search.Registration.Response">
    <Status>SUCCESS</Status>
    <Providers>
    <Provider>
    <Message>Amazon.com offers the Earth's Biggest Selection of products. With this demonstration 
    application, you to explore the products of Amazon from the comfort of Microsoft Office.</Message>
    <Id>{57EECFA5-4314-40af-827B-9688A0579306}</Id>
    <Name>Amazon.com</Name>
    <QueryPath>http://localhost/AmazonResearch/Query.asmx</QueryPath>
    <RegistrationPath>http://localhost/AmazonResearch/Registration.asmx
    </RegistrationPath>
    <AboutPath>http://localhost/AmazonResearch/AboutService.aspx
    </AboutPath>
    <Type>SOAP</Type>
    <Services>
    <Service>
    <Id>{B333CC52-55FB-4f26-9B55-3CAA6AB21FFA}</Id>
    <Name>Books on Amazon.com</Name>
    <Description>Welcome to the Amazon Product Catalog Search Utility for Microsoft Office 2003. 
    This demonstration application will allow you to explore the products of Amazon from the comfort 
    of Microsoft Office.</Description>
    <Copyright>Amazon.com, Inc. Copyright (c) 2003.</Copyright>
    <Display>On</Display>
    <Category>ECOMMERCE_GENERAL</Category>
    </Service>
    </Services>
    </Provider>
    </Providers>
    </ProviderUpdate>
    </string>
    

    If this is not the response, see your administrator to confirm that the .NET Framework and ASP.NET are functioning correctly. On some systems, you must register ASP.NET with IIS. See the command-line utility installed with the .NET Framework: aspnet_regiis.exe found in the .NET directory. (As an example, type the following command in a command prompt: C:\WINNT\Microsoft.NET\Framework\v1.0.3705\aspnet_regiis.exe)

Configuring the Smart Tag Component of the Amazon.com Research Service

The research task pane uses a client-side smart tag to provide a richer set of features that interact with documents. If the smart tag is not installed, the research task pane continues to function normally with the exception that the smart tag menu items are not available.

To install the smart tag on a client computer:

  1. Open a command prompt. Assuming that the Amazon.com files are located in the C:\Amazon\ResearchLibrary\, type the following:

    CD C:\Amazon\ResearchLibrary
    
  2. Register the smart tag DLL with the following command-line command:

    regsvr32 AmazonResearch.dll
    

    A message box appears stating, "DllRegisterServer in AmazonResearch.dll succeeded."

    Click OK.

  3. To notify Office that this smart tag is available, type the following at the command line:

    regedit RegistrySetup.reg
    
  4. You are prompted to confirm that you want to add this information into the registry. Click Yes.

    A message box appears stating, "Information in RegistrySetup.reg has been successfully entered into the registry."

    Click OK.

  5. Restart the client computer.

Registering the Amazon.com Research Service

  1. To add the Amazon.com research service in Office 2003 (using Word as an example):

  2. Start Word.

  3. Open the research task pane.

    1. Ensure that the task pane is visible. If not, select Task Pane from the View menu.
    2. Click the Task Pane list and select Research. The research task pane should now be visible.
  4. At the bottom of the research task pane, click the Research options link. This opens the Research Options dialog box.

    Figure 7. The Research Options dialog box

  5. Click Add Services. The Add Services dialog box is displayed.

    Figure 8. The Add Services dialog box

  6. In the Address text box, type in the address of the research task pane registration page. For the general directions provided in this document, this path is as follows:

    http://localhost/AmazonResearch/Registration.asmx
    
  7. At this point, follow the prompts until the service is installed.

  8. Finally, return to the research task pane and select the Books on Amazon.com site from the list of research sources.

    Figure 9. The research task pane

Conclusion

The Amazon.com research service provides the user with unlimited access to a vast repository of books as well as key information on those books. This Web service, combined with features of the Office Research Services feature, significantly improves the power that users have at their disposal for manipulating this data. These features include the ability to place text inside a document or worksheet with just the click of a button, and the use of smart tags. As a developer armed with these powerful tools and the knowledge of how they work, you are prepared to craft your own research service provider that is tailored to the needs of your organization.

Additional Resources

This article describes research services from the viewpoint of a developer. For additional information on Office Research Services, see the following sources:

© Microsoft Corporation. All rights reserved.