Click to return to the Server Technologies home page    
Web Workshop  |  Server Technologies

Creating an Active Server Component in Visual Studio 97


Wayne Berry
Editor, 15 Seconds

June 1997

Contents
Introduction
Step 1: Creating an ATL project
Step 2: Adding an Active Server Component
Step 3: Creating a method
Step 4: Making it work
Step 5: Adding the component to an ASP page

Introduction

The first in a four-part series, this article provides a simple example of how to create an Active Server Component in Visual Studio™ 97 using the Active Template Library (ATL) version 2.0 and Microsoft Visual C++® version 5.0. The Active Server Component works with Active Server Pages (ASP) as an extension of Active Server. When placed within an ASP page, the component we are going to create will get the user's cookie, or assign a cookie if the user doesn't have one.

If you are still using Microsoft Visual C++ version 4.2, we recommend you upgrade to Microsoft Visual Studio 97, which makes COM objects much easier to program.

If you don't plan to use ASP technologies, you may want to consider using a simple object, instead. Please see the discussion at the end of this article.

Creating an Active Server Component consists of five steps. If you have already installed Visual Studio 97, you can get started right away.

Back to topBack to top

Step 1: Creating an ATL Project

The first step in building an Active Server Component is to create an ATL project. We will create the ATL project using the ATL COM AppWizard. The code produced by the wizard is nothing more than a DLL shell, with the ability to register the objects that will be defined later, to create those objects through a class factory, and to build a DllMain that supports the DLL shell. The shell by itself is not an Active Server Component. The shell is a holder for many Active Server Components. These components share the same code that is available in the shell and can share functionality between them, since they exist in the same DLL.

  1. Open Microsoft Visual Studio™ 97; from the File menu, choose New.
  2. Click the Project tab.
  3. From the list of projects, choose ATL COM AppWizard.
  4. For the project name, type SMUM. It is important to note that this is the name of the project, not the name of the object. We like to give the project the same name as that of the company under which the project will be licensed. In this example, "SMUM" stands for "Sign Me Up Marketing." We also prefer to use the project name as the first part of the object's common name. For instance, the Prog ID of the object in this example will be SMUM.Example. We prefer not to give the project the same name as the object, because there may be more than one object within a project/shell. Also, the ATL wizard will have problems with filenames later if the object and the project have the same name.

    Figure 1. New project

  5. Press OK. The "ATL COM AppWizard - Step 1 of 1" dialog box will appear.
  6. Check Allow merging of proxy/stub code.
  7. Click Finish.
  8. Click OK.

    Figure 2. ATL COM AppWizard - Step 1 of 1

Files affected

TopBack to top

Step 2: Adding an Active Server Component

The second step is to add an Active Server Component to the shell. This is done using the ATL Object Wizard.

  1. From the Visual Studio menu bar, choose Insert, and click New ATL Object. The "ATL Object Wizard" dialog box will appear.
  2. Choose Component from the left-hand list box.
  3. In the right-hand list box, choose the type of component: ActiveX Server Component.

    Figure 3. ATL Object Wizard

  4. Click Next. Another dialog box, "ATL Wizard Object Properties," will appear.
  5. For Short Name, type the name of the component. In this example, we will call the component Example. Notice that while you type, the wizard automatically fills in the other properties.
  6. Type the object's Prog ID by highlighting the first part of the Prog ID and replacing it with SMUM (in step one, I mentioned that the example object Prog ID would be "SMUM.Example"). Be careful not to highlight the period that separates the two parts of the Prog ID. The wizard will not allow you to type a period in this edit box. If you delete the period, you will have to start over from step 1.
  7. Modify the Type to read Sign Me Up Marketing Example Component, so that when you test the component from the ActiveX Control Test Container, you will be able to find it quickly. The ActiveX Control Test Container is a tool that comes with Visual C++ installation of Visual Studio 97. The test container loads COM objects and allows you to test them by their method and properties.

    Figure 4. ATL Wizard Object Properties

  8. Click OK to create the object.

Files affected

The ATL Object Wizard creates these files:

The wizard also adds code describing the interface of the Example object to the project's SMUM.IDL.

Object Member Variables

When the ATL Object Wizard creates an Active Server Component, it adds by default the member variables that interface with the Application, Request, Response, Server, and Session objects.

TopBack to top

Step 3: Creating a Method

Now we will implement a method to get and set a cookie. First, we need to add a method called GetCookie to the Example interface.

  1. Go to the Project window of the SMUM project.
  2. Click the Class View tab.
  3. Expand the Cexample class until you can see the IExample interface.
  4. Right-click IExample, and choose Add Method.

    Figure 5. Adding a method in the Project view

  5. The "Add Method to Interface" dialog box should appear.
  6. For Method Name, type GetCookie.
  7. For Parameters, type [out,retval] BSTR *pVal.

    Figure 6. Adding a method to the interface

  8. Click OK.

By using Visual Studio 97 to add a method in the Project view, you automatically insert the interface description into the *.IDL file, and supporting code for the class is generated.

TopBack to top

Step 4: Making It Work

Now we are going to add the functionality to the GetCookie method created in step 3. This method gets the current cookie; if not available, it creates a cookie and returns its value.

To quickly find the section of code where Visual Studio added the method, expand the project in the Project window, expand the IExample interface, and double-click on the GetCookie method. Add the following code:

STDMETHODIMP CExample::GetCookie(BSTR * pVal)
{
   GUID            guid;
   TCHAR           lpszCookie[25];
   VARIANT            varOptional;
   HRESULT            hr;

   // Check the Pointer 
   if (pVal==NULL)
      return E_POINTER; 

   // Optimistically set the Returning 
   // HRESULT
   hr=S_OK;

   // Configure the Optional Variant
   // This configuration follows
   // the documentation for the
   // Active Server Object Interfaces
   V_ERROR(&varOptional) == DISP_E_PARAMNOTFOUND;

   // Check to make sure all the interfaces
   // loaded correctly.  This is the part
   // that would fail if you called this
   // object from Visual Basic.

   if (m_bOnStartPageCalled)
   {
      // Allocate the Request Dictionary Objects
      // so that we can pass them to the Active
      // Server Object Interfaces
      CComPtr<IRequestDictionary> piReadDict;
      CComPtr<IRequestDictionary> piWriteDict;

      // Pointer to the Write Cookie Interface
      IWriteCookie      *piWriteCookie;
   
      // From the Request Interface retrieve
      // a Dictionary of Cookies
      hr=m_piRequest->get_Cookies(&piReadDict);

      if (FAILED(hr))
         return hr;

      // Allocate and set Variants to pass to 
      // the dictionary
      CComVariant vtIn(_T("GUID"));
      CComVariant   vtOut;

      // Get the Cookie from the dictionary
      // named SMUMID and put it in vtOut
      hr = piReadDict->get_Item(vtIn, &vtOut);

      if (FAILED(hr))
         return hr;

      // vtOut contains an IDispatch Pointer. 
      // To fetch the value for the cookie, you
      // must get the Default Value for the 
      // Object stored in vtOut using VariantChangeType.  
      hr = VariantChangeType(&vtOut, &vtOut, 0, VT_BSTR);

      if (FAILED(hr))
         return hr;

      // If the Cookie isn't set then
      // the length of vtOut will be 0
      if (!wcslen(V_BSTR(&vtOut)))
      {
         // There isn't a Cookie assigned
         // so we have to create one.

         // Use a GUID for the Unqiue User Cookie
         CoCreateGuid(&guid);

         // Put the GUID into a String
         wsprintf(lpszCookie,
            _T("%X%X%X%X%X%X%X%X%X%X%X"),
            guid.Data1,
            guid.Data2, guid.Data3,
            guid.Data4[0], guid.Data4[1],
            guid.Data4[2], guid.Data4[3],
            guid.Data4[4], guid.Data4[5],
            guid.Data4[6], guid.Data4[7]);
      

         // Create a Variant from the String
         CComVariant   vtCookieValue(lpszCookie);

         // Get the writable Cookie Dictionary 
         // from the Response Object.  Notice
         // that it is the Response Object and
         // not the Request Object like above.
         hr=m_piResponse->get_Cookies(&piWriteDict);

         if (FAILED(hr))
            return (hr);

         // Get the Writable Interface for
         // the GUID cookie from the Cookie Dictionary
         // Notice here that the GUID cookie
         // doesn't exist in the dictionary, but we
         // can still ask for its interface
         hr = piWriteDict->get_Item(vtIn, &vtOut);

         if (FAILED(hr))
            return(hr);

         // Make it easy on ourselves, get and cast
         // the IDistpach value to the dual interface
         // that we want to call
         piWriteCookie = (IWriteCookie*)(vtOut.ppdispVal);
         
         // Create the Cookie using the Name GUID and the Value of
         // the GUID that we generated
         hr = piWriteCookie->put_Item(varOptional,
            V_BSTR(&vtCookieValue));

         if (FAILED(hr))
            return(hr);

         // Create an Expiration Date sometime in the future
         // To keep the example simple, I am using my birthday
         // in 2010. 10/4/2010. Some other time, we will show
         // how to generate a VB DATE (double) without MFC.
         DATE   dtExpiration=40455.0;
         
         // This is required if you want the browser
         // to retain the cookie between sessions
         hr = piWriteCookie->put_Expires(dtExpiration);

         if (FAILED(hr))
            return(hr);

         // Assign the out,retval
         *pVal = ::SysAllocString(V_BSTR(&vtCookieValue)); 

         // A little help for Debugging
         ATLTRACE (_T("Setting Cookie to: %s\n"),
            V_BSTR(&vtCookieValue));
      }
      else
      {
         // Assign the out,retval
         *pVal = ::SysAllocString(V_BSTR(&vtOut));
         
         // A little help for Debugging
         ATLTRACE (_T("Cookie was set to: %s\n"),
            V_BSTR(&vtOut));
      }
   }

   return hr;

 }

Once you have added the code, build the DLL. After compilation and linking, Visual Studio will register the DLL. If you have Internet Information Server (IIS) installed, you can now use the component.

TopBack to top

Step 5: Adding the Component to an ASP Page

The last step is to add the component you just built to your ASP page.

The right way

Here is the right way to call the Active Server Component. Notice that the cookie header precedes the <HTML> and <BODY> tags.

<%
' Here is where the OnStartPage method is called
Set Example=Server.CreateObject("SMUM.Example.1")

'Here is the method call to assign the cookie
Cookie = Example.GetCookie()
%>
<HTML>
<BODY>
<%=Cookie%>
</BODY>
</HTML>

Warning

When setting cookies, you must always assign the cookie header before the main body (<HTML> and <BODY> tags) of your page. For instance, this is the wrong way to send the code:

<HTML>
<BODY>
<%
Set Example=Server.CreateObject("SMUM.Example.1")

' The Cookie Header is set with this
' call if a cookie doesn't exist,
' which is too late since some of the body text has
' been sent already.
Cookie = Example.GetCookie()
%>
The Cookie is : <%=Cookie%>
</BODY>
</HTML>

Components or Simple Objects?

We just ran through the steps to create an Active Server Component using the ATL Object Wizard, which inserted code and created files to support the COM object named Example. This is the same code that the wizard would have created if we had chosen to create a simple object.

Simple objects are sometimes more appropriate for large projects if you plan to use the same object over and over, and that object won't require access to Active Server Component interfaces (Application, Request, Response, Server, and Session). When creating an Active Server Component, the wizard adds code to access those interfaces. This is convenient only if the component is going to be used in an Active Server environment.

A cookie example

If you want to create a customer object based on a cookie, you can call an Active Server Component from an ASP page; for example:

Set Creator=Server.CreateObject("SMUM.CustomerCreator.1")
Set Customer = Creator.CreateCustomer
Customer.EmailAddress = "webmaster@signmeup.com" 

The cookie call to the Request object is hidden from the ASP developer. When the CustomerCreator object performs the CreateCustomer method, the cookie value is retrieved from the Request interface within the CustomerCreator object. If the user does not currently have a cookie, the Creator object also sets the cookie in the Response interface. Finally, the e-mail address is written to a database, with the cookie serving as the primary key.

Consider this example in which the Creator object is a simple object instead of an Active Server Component.

Set Creator=Server.CreateObject("SMUM.CustomerCreator.1")
Set Customer = Creator.CreateCustomer(Request.Cookies("SMUMID")) 
Response.Cookies("SMUMID") = Customer.Cookie
Customer.EmailAddress = "webmaster@signmeup.com" 

In this example, we have to pass the cookie in the Creator object and assign the cookie once we have created the Customer object. This technique makes more work for the programmer. If it is more work, why is it better to create a simple object? The answer lies within the scope of the project.

For instance, once all the e-mail addresses have been collected from the Web site, you may want to view each customer address from your desktop. With the simple object, you can create a Visual Basic® application that views each e-mail address in turn. However, you cannot call the Active Server Component from Visual Basic, because the instantiation of the Active Server interfaces would fail.

Simple objects can be called from Visual Basic and from ASP pages; Active Server Components can be called only from ASP pages. For a little more work, you can get a lot more use out of your object.

Wayne Berry is the editor of 15 Seconds Non-MS link, an online publication written for Internet developers and content providers about IIS, ISAPI, and Active Server. For more information about 15 Seconds, contact the Webmaster.



Back to topBack to top

Did you find this material useful? Gripes? Compliments? Suggestions for other articles? Write us!

© 1999 Microsoft Corporation. All rights reserved. Terms of use.