Programmer to ProgrammerTM  
Wrox Press Ltd  
   
  Search ASPToday Living Book ASPToday Living Book
Index Full Text
 
ASPToday Home
 
 
Home HOME
Site Map SITE MAP
Index INDEX
Full-text search SEARCH
Forum FORUM
Feedback FEEDBACK
Advertise with us ADVERTISE
Subscribe SUBSCRIBE
Bullet LOG OFF
                         
      The ASPToday Article
August 24, 2000
      Previous article -
August 23, 2000
  Next article -
August 25, 2000
 
   
   
   
You [too] Can Develop a C++ ATL Component   James Brannan  
by James Brannan
 
CATEGORIES:  Components, XML/Data Transfer  
ARTICLE TYPE: Overview Reader Comments
   
    ABSTRACT  
 
Article Rating
 
   Useful
  
   Innovative
  
   Informative
  
 69 responses

ATL is used to produce small, fast, industrial strength components. It also calls methods very quickly and allows you a fine control over COM features. In this article, James Brannan explains how easy it is to create a useful ATL C++ component that translates ADO to XML. By including a parameter as a path to an XSL template, this component can return any type of output an XSL style sheet is capable of producing.

   
                   
    Article Discussion   Rate this article   Related Links   Index Entries  
   
 
    ARTICLE

Microsoft has spent a great deal of time and money trying to ensure that you can program a Visual C++ ATL component. These components are easier to program than you think. In this article, we will create an ATL C++ component – one that is actually useful for web developers. Along the way, we will discuss strings, exception handling, ActiveX Data Objects (ADO), the C++ Standard Template Library (STL), and Microsoft’s XML parser (MSXML). If you are a VB programmer and have taken a C++ course in the past then by the end of this tutorial you should be able to program simple (but useful) Visual C++ ATL COM objects.

Visual C++ programmers will find much "behind the scenes" explanation of the code lacking and perhaps overly simplistic. My goal is not to present an exhaustive treatise of Visual C++ ATL, but rather, to convince ASP and VB COM developers that an in–depth knowledge of C++, COM, and ATL is not necessary to begin using it effectively.

Why C++?

Why would you ever need to use C++? VB, after all, is a much easier tool for creating COM objects. By developing something in C++ you are increasing both cost of development and cost of maintenance. Moreover, C++ programmers are harder to find and more costly to hire. And who will maintain the code once finished? So with these strikes against C++ why use it?

Efficiency and speed. Despite advances in VB, Java, and scripting languages such as ASP, C++ remains the champion in speed and efficiency. Although hardware improvements have lessened the validity of the speed/efficiency argument for the desktop, in the Web Server world, where suddenly there are tens, hundreds, or possibly thousands of concurrent users – speed and efficiency remains critical. On a high–traffic site, something as simple as creating a large text string can bring a web server to its knees when using ASP.

The Design of Our Component

Let’s design a C++ ATL component that obtains an ADO recordset, creates an XML string, processes the string, and then returns HTML. (For introductory articles on XML and XSL, please check out the articles on ASPToday under the XML category. In this article, we only concern ourselves with creating a useful C++ ATL component).

Assume we cannot use ADO’s PersistAsXML save option. Microsoft’s ADO 2.1 and ADO 2.5 provide easy ways of converting ADO recordsets to XML. However, Microsoft’s output is verbose and may not necessarily be in the format desired. Assume our XML dataset must appear in a format similar to:

<records>
<record><myfield>test1</myfield></record>
<record><myfield>test2</myfield></record>
</records>

We must generate the XML ourselves.

David Sussman’s article, Creating XML Recordsets describes how to create an XML recordset using ASP. You loop through the records and fields concatenating the field name and value to a large string. Easy enough, but consider the scalability of string concatenation when using VB or ASP. Each concatenation to the string requires allocating new memory and then copying the old string and the string to concatenate to the new memory location.

During a recent project, I implemented that solution. It worked well provided I used small recordsets. As the recordset size increased, performance degraded significantly. I posted my dilemma to VBXML.com’s XML mailing list. One helpful piece of advice I received was to pre–allocate the memory required for the XML string. This avoids the cost of dynamically reallocating memory every time we append to the string. I took it one step further and decided to program it as a C++ ATL component.

The Implementation

Start Visual C++ and from the File menu choose New . Select ATL COM AppWizard , enter atlProject as the project name, choose Create new workspace , and click OK .

From the ATL COM AppWizard dialogue box choose Dynamic Link Library (DLL) – be sure not to select any other options – and click Finish . Review the New Project Information dialogue box and click Finish if satisfied.

We now add an ATL Object to the project. Right-click on atlProject in the ClassView pane and select New ATL Object from the pop-up menu.

From the ATL Object Wizard dialogue select Simple Object and click Next . Enter the name xmlWrapper in the Short Name text box on the Names tab. On the Attributes tab check Both for the Threading Model , Dual for the Interface , and Yes for Aggregation . Also, check ISupportErrorInfo and FreeThreadedMarshaller . Click OK and the wizard adds the object to our project.

Checking ISupportErrorInfo adds this interface to our object, allowing us to provide error information to calling applications. I am not sure why we check the other options. We will be using our component as an Apartment threaded component; however, MSDN (Agility in Server Components) suggests that we use these options for components used in ASP pages. If my admission of ignorance leaves you unsettled, refer to Dr. Grimes’s Book, Professional ATL COM Programming, for in depth discussion on these options.

The next step is to add a public method to the ATL object. Right–click on IxmlWrapper in the ClassView pane and select Add Method from the pop–up menu. In the Add Method to Interface dialogue enter getOutput as the method’s name and enter:

BSTR *pDataSourceName, BSTR *pSQL, BSTR *pDocTypeDef, BSTR *pXSLTemplate, [out, retval]  BSTR *pReturnedData

as the parameters, where *pDataSourceName is an ODBC Data Source, *pSQL is a SQL statement, *pDocTypeDef is a path to an XML DTD, and *pXSLTemplate is a path to an XSL style sheet. The parameter *pReturnedData is the variable used to provide the data to the client calling our method. Click OK and the method is added to IxmlWrapper .

From the FileView pane, expand the Source Files folder and double-click x mlWrapper.cpp to show the code. We generated two methods. Selecting Support ISupportErrorInfo on the Attributes tab of the ATL Object Wizard Properties dialogue generated code necessary for using error handling.

STDMETHODIMP CxmlWrapper::InterfaceSupportsErrorInfo(REFIID riid)
{
   static const IID* arr[] = 
   {
      &IID_IxmlWrapper
   };
   for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
   {
      if (InlineIsEqualGUID(*arr[i],riid))
         return S_OK;
   }
   return S_FALSE;
}

This code has something to do with the IDL and allows our component to return errors. Because you can use many different languages to program a COM object’s client application, the COM object cannot throw a C++ exception. To pass rich error handling information back to a client we must use error objects. The code generated by ISupportErrorInfo allows us to create these objects.

The ATL Wizard also generated the beginnings of our method:

STDMETHODIMP CXmlWrapper::getOutput(BSTR *pDataSourceName, BSTR *pSql, BSTR *pDocTypeDef, BSTR *pXSLTemplate, BSTR *pReturnedData)
{
   // TODO: Add your implementation code here
   return S_OK;
}

The text STDMETHODIMP is a pre-processor macro that the IDL processor uses to generate the return type of the method, an HRESULT . Since our COM method implements a dual interface, it must return an HRESULT that tells the object’s client if it worked ( S_OK ) or failed ( E_FAIL ). Our method places the return data in *pReturnedData , but returns an HRESULT . This will be important when we add error handling.

Importing the Libraries

We need to import the ADO and MSXML header files. We also need to include the Standard Template Library’s (STL) String template class. Below #include "xmlWrapper.h" add the following lines of code:

#include <string>
   using namespace std;
#import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename("EOF", "adoEOF")
#import "c:\windows\system\msxml.dll" rename_namespace("xml")

The path to these files will probably be different on your system. Find the correct locations and enter those paths.

A good reference for using MSXML via the #import directive is A Flying Start to Programming IE5/XML using C++ & COM by Richard Anderson at comdeveloper.com. In his article, he describes working with MSXML’s object hierarchy much more thoroughly than here. Here we are building our XML as a string; in his article, he builds XML node by node using MSXML.

A good reference for using ADO via the #import directive is Lynn Robinson’s article Much ADO About Data Access in Visual C++ Developer’s Journal. As she explains, the #import directive "generates header files containing typedef declarations, smart pointers for interfaces, and enumerated constants". Working with pointers can be very tricky. One wrong assignment and you have a memory leak. Smart pointers avoid this problem.

Smart pointers provide wrappers around a DLL’s interface pointers, maintain reference counts to the interface pointers for you, handle throwing COM errors when one of the DLL’s methods returns an HRESULT of E_FAIL , and provide a friendly interface for working with the DLL’s methods and properties. Using the #import directive makes working with other COM libraries much easier.

We now add the smart pointers needed to access ADO and MSXML. In getOutput add the code:

   _RecordsetPtr spRecordSet;
   _ConnectionPtr spConnection;
   FieldPtr spField;
   _variant_t vtEmpty;

   xml::IXMLDOMDocumentPtr spXmlDocument;
   xml::IXMLDOMDocumentPtr spXslDocument;
   xml::IXMLDOMParseErrorPtr spXmlError;

This code provides the variables we needed for ADO’S Connection object, Recordset object, and Field object. It also provides the variables needed for MSXML’s DOMDocument object and XMLDOMParseError object.

Error Handling

Before we start adding detail to our component, we should add error handling. We need to add a C++ try/catch block. After adding the error handling code, getOutput should appear:

STDMETHODIMP CxmlWrapper::getOutput(BSTR *pDataSourceName, BSTR *pSQL, BSTR *pDocTypeDef, BSTR *pXSLTemplate, BSTR *pReturnedData)
{
   _RecordsetPtr spRecordSet;
   _ConnectionPtr spConnection;
   FieldPtr spField;
   _variant_t vtEmpty;

   xml::IXMLDOMDocumentPtr spXmlDocument;
   xml::IXMLDOMDocumentPtr spXslDocument;
   xml::IXMLDOMParseErrorPtr spXmlError;

   HRESULT HR = S_OK;
   try
   {
   }
   catch(_com_error &e)
   {
      HR = E_FAIL;
   }
   catch(xml::IXMLDOMParseErrorPtr spXmlError)
   {
      HR = E_FAIL;
   }
   catch(...)
   {
      HR = E_FAIL;
   }
   return HR;
}

If you have taken a C++ course, the try/catch block should be familiar to you. We have set up three catch blocks. The first is to catch COM specific errors thrown by the COM objects we are using. The second is to catch XML parser errors thrown by MSXML. The third is to catch unspecified errors. Remember, our method returns an HRESULT . If the method fails, we send a HRESULT indicating failure. We will expand the error handling when we add the details to our method.

Setting up the Variables

We now set up a few variables needed for connecting to the database and creating the XML string. Add the code:

basic_string<WCHAR> wstrXmlString;
CComBSTR ccbstrConnection;
CComBSTR ccbstrQuery;
CComBSTR ccbstrTemplate;
long lngFieldCount;
long lngIndex = 0;

We are using the String class from the STL because of the rich methods it provides to work with, the most important being a method to pre-allocate memory space needed. CComBSTR is a thin wrapper class for BSTR, COM’s basic string datatype. We could probably make the component more efficient by not using CComBSTR ’s; however, they make working with BSTR’s significantly easier.

Getting the Dataset

We now need to get the data. Add the following lines to the try block of your code:

spConnection.CreateInstance(_uuidof(Connection));
spRecordset.CreateInstance(_uuidof(Recordset));
ccbstrConnection.Append(*pDataSourceName);
ccbstrQuery.Append(*pSQL);
ccbstrTemplate.Append(*pXSLTemplate);
spConnection->Open(ccbstrConnection.m_str, L"", L"", -1);
spRecordset = spConnection->Execute(ccbstrQuery.m_str, &vtEmpty, adCmdUnknown);
lngFieldCount = spRecordset->Fields->GetCount();

The first two lines create new instances of their respective objects. The next three lines simply assign the parameters to their respective variables we created. Then we open the connection and then set the recordset equal to the result set of the connection object. Note that unlike VB, blank parameters cannot be left blank therefore we create an instance of the _variant_t class, vtempty and make it empty. We then set lngFieldCount to the number of fields in the recordset.

Building the XML String

Now that we have the recordset, we need to build the XML string. We are using the String class from the STL. Except we are using WCHAR , as we are using Unicode and therefore need to use "wide" characters. Since Windows 95/98 does not support Unicode, we will compile the code as Win32 and note Win32 Unicode – if you are following along using Windows NT or Windows 2000 compile to Win32 Unicode (more on this later).

For more details on Unicode and wide characters, see Chapter One of Dr. Grimes’s book.

We need to allocate space for the string. Add this code to the try block:

wstrXmlString.reserve(8500 * sizeof(WCHAR));

Pre-allocating the space needed in memory is more efficient because rather than having to reallocate memory each time we append, we simply fill the space previously allocated until full. We choose the size 8500 arbitrarily; but it may need it to be bigger or smaller, depending on the size of your dataset. Because we are using wide characters, we need to double whatever we estimate the character count to be.

We then begin our XML recordset. Add the prolog statement, the DOCTYPE statement containing a reference to our DTD, and begin the root tag, <records> :

wstrXmlString.append(L"<?xml version=\"1.0\" standalone=\"no\"?>");
wstrXmlString.append(L"<!DOCTYPE records SYSTEM \"");
wstrXmlString.append(*pDocTypeDef);
wstrXmlString.append(L"\">");
wstrXmlString.append(L"<records>");

Note that L has the effect of specifying that this is a wide character string. The \ (escape) character allows us to treat the " as a character in the string.

Looping through the Records

We loop through the records and append the field name and value for each field. As a VB programmer, this code should look familiar to you. The biggest difference you should see is the -> character. C++ uses the -> character when accessing an object’s method with a pointer:

spRecordset->MoveFirst();

while(!(spRecordset->adoEOF))
{
   wstrXmlString.append(L"<record>");
   for (lngIndex = 0; lngIndex < lngFieldCount; lngIndex++)
   {
spField = pRs->Fields->GetItem(lngIndex);
      wstrXmlString.append(L"<");
      wstrXmlString.append(_wcslwr(spField->GetName()));
      wstrXmlString.append(L">");

      if (spField->GetActualSize() > 0)
      {
         wstrXmlString.append(_bstr_t(spField->GetValue()));
      }
      wstrXmlString.append(L"</");
      wstrXmlString.append(_wcslwr(spField->GetName()));
      wstrXmlString.append(L">");
   }

   wstrXmlString.append(L"</record>");
   spField.Release();
   spRecordset->MoveNext();
}
      
wstrXmlString.append(L"</records>");
spRecordset->Close();
spConnection->Close();

We move to the first record then loop through the records until we are at adoEOF . With each loop, we begin our XML record with a <record> tag. Then we build that record by looping through each field in the record. First, we append the field name as an opening tag, then the field value, and finally the field name as a closing tag. After appending all fields, we append the closing record tag and move to the next record. After completing all records, we close the record tag and are finished building the string. We then close the recordset and the connection.

Loading into MSXML

We now examine the variable ccbstrTemplate . If the template indicates xml then we return the XML, otherwise we instantiate two instances of the MSXML DOMDocument class and use them to transform the XML to the desired format. Below the lines closing the recordset and connection, add the code:

if (ccbstrTemplate == L"xml")
{
   *pReturnedData = SysAllocString(wstrXmlString.c_str());
}
else
{
long lngErrorCode = 0;
   spXmlDocument.CreateInstance(_uuidof(xml::DOMDocument));
   spXmlDocument->put_validateOnParse(true);
   spXmlDocument->loadXML(wstrXmlString.c_str());
   spXmlDocument->get_parseError(&spXmlError);
   spXmlError->get_errorCode(&lngErrorCode);

   if(lngErrorCode != 0) throw(pXmlError);

   spXslDocument.CreateInstance(_uuidof(xml::DOMDocument));
   spXslDocument->load(_variant_t(ccbstrTemplate.m_str));
   spXslDocument->get_parseError(&spXmlError);
   spXmlError->get_errorCode(&lngErrorCode);

   if (lngErrorCode != 0) throw(spXmlError);

*pReturnedData = SysAllocString(spXmlDocument->transformNode
(spXslDocument));
   spXmlDocument->get_parseError(&spXmlError);
   spXmlError->get_errorCode(&lngErrorCode);
      
   if (lngErrorCode != 0) throw(spXmlError);
         
}

If the XSL template’s value is xml we return XML using the statement *pReturnedData = SysAllocString(wstrXmlString.c_str()); .we are not really ‘returning’ the XML string but allocating a new string and copying the passed string into it. We are then copying the string to the memory location pointed to by pReturnedData , hence, *pReturnedData .

If the value of ccbstrTemplate is a path to an XSL template then we have more processing to do. After creating an instance of the DOMDocument, we load the XML string into it. Since our XML refers to a DTD, we turn validation on. We also want to check for errors. We do this by checking the error code of the DOMDocument, looking for a number other than zero. If a value other than zero occurs then we know an error occurred and we throw an error, passing spXmlError . After loading the XML to the DOMDocument successfully, we do the same for the XSL template.

After both documents are loaded, we transform the XML DOMDocument using the XSL DOMDocument with the code:

*pReturnedData = SysAllocString(spXmlDocument->
transformNode(spXslDocument));
spXmlDocument->get_parseError(&spXmlError);
spXmlError->get_errorCode(&lngErrorCode);

if (lngErrorCode != 0) throw(spXmlError);

We place the results of the transformation directly into pReturnedData using SysAllocString and we are finished.

Error Handling

We left error handling incomplete. Add:

if( spRecordset ) if(spRecordset->GetState() > 0) spRecordset->Close();
if( spConnection ) if(spConnection->GetState() > 0) spConnection->Close();

to all three of the catch blocks. This code should be self explanatory – if the recordset or connection is open, close them. To the catch block catching COM errors add the code:

CComBSTR errDescription;
errDescription = e.Source().copy();
errDescription.Append(L" ");
errDescription.AppendBSTR(e.Description());
Error(errDescription, e.GUID(), HR);

below the two lines checking the status of the recordset and connection. This code first creates a string describing the error that was thrown, and then throws a new COM error for the calling application to catch. There are certainly more correct ways to throwing an error from a C++ ATL component, but this method works without a problem. Add the following code to the catch block catching the XMLParseError.:

long lngLine = 0;
long lngLinePos = 0;
CComBSTR ccbstrBuiltError;

ccbstrBuiltError.Append(L"Error parsing XML. ");
ccbstrBuiltError.AppendBSTR(spXmlError->Getreason().copy());
ccbstrBuiltError.Append(L" ");
ccbstrBuiltError.AppendBSTR(spXmlError->Geturl().copy());

spXmlError->get_line(&lngLine);
spXmlError->get_linepos(&lngLinePos);

if (lngLine != 0 && lngLinePos != 0)
{
ccbstrBuiltError.Append(L" line: ");
   ccbstrBuiltError.AppendBSTR(_bstr_t(lngLine));
   ccbstrBuiltError.Append(L" position: ");
   ccbstrBuiltError.AppendBSTR(_bstr_t(lngLinePos));
}

Error(ccbstrBuiltError.m_str, IID_IxmlWrapper);      

This code accomplishes the same thing as the catch block that catches COM errors; however, we add some conditional logic to suppress returning line numbers and positions from XML error messages when these values are zero.

Finally, add:

CComBSTR ccbstrBuiltError;
ccbstrBuiltError.Append(L"An Unspecified Error Occurred. ");
Error(ccbstrBuiltError.m_str, IID_IxmlWrapper);

to the catch block catching unspecified errors. Consult the code support files in the download for this article to see what the finished method, in xmlWrapper.cpp , should look like.

Preparing for Compilation

Since we are using C++ exception handling we need to enable it in our project’s settings. First, ensure that the active configuration is Win32 Debug by selecting Set Active Configuration from the Build menu and ensuring Win32 Debug is highlighted.

Next, from the Project menu select Settings and click the C/C++ tab. Choose C++ language from the dropdown and check enable exception handling . Click OK . We are now ready to compile and debug our component.

Compiling

From the Build menu select Build xmlWrapper.dll and hopefully it compiles. If an error occurs, it appears in the bottom window. If you then click on the error, Visual C++ highlights the line and points to the line of code the error occurs with a small blue arrow. After fixing all the errors and the file actually compiles it is time to debug the DLL.

Debugging using Visual Basic

An easy way to debug your component is by creating a VB executable. Our component is taking an XML dataset, validating that dataset using a DTD, and then applying an XSL stylesheet to that dataset to transform it. We need to create a simple DTD, XSL template, Access database, and a VB executable (all available in this article’s supporting download).

Let’s set up a simple DTD and XSL template. Using Notepad create the file simple.dtd , containing the code:

<?xml version="1.0"?>
<!ELEMENT record(myfield)>
<!ELEMENT myfield (#PCDATA)>

This DTD indicates that the XML recordset contains record elements that in turn contain one element, myfield . The element myfield contains text data. The XSL template, simple.xsl , is also straightforward. Create a file simple.xsl with the code:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">
<xsl:template match="/">
   <xsl:for-each match="record">
      <xsl:value-of select="myfield"/><br/>
   </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

This code loops through each record and prints myfield ’s value.

After creating the DTD and the XSL stylesheet, we need to create a simple Access database and register it as an ODBC datasource. Create the database simple.mdb with a single table and a single field named myfield . Enter three or four records to the table putting whatever data you wish in the myfield field. After you are completed, register the database as an ODBC datasource with the name simple .

VB is an easy tool to use to debug our component. Like VB, in Visual C++ we can specify an application for debugging our COM object. We will need to make our project reference the compiled debug version of our C++ DLL (atlProject 1.0 Type Library). Start VB and select Standard EXE as the project type. Make your project also references MSXML.DLL (Microsoft XML, version 2.0). Add the RichTextBox control to the Toolbox. Add a RichTextBox to your form and add the following code to the Form_Load method:

Dim myobj As ATLPROJECTLib.xmlWrapper

Dim theDTD As String
Dim theXSL As String

Set myobj = New xmlWrapper

theDTD = "c:\windows\desktop\atl_component\simple.dtd"
theXSL = "C:\WINDOWS\Desktop\atl_component\simple.xsl"

RichTextBox1.Text = _
    myobj.getOutput("simple", "Select * from simple", theDTD, theXSL)

Compile the executable and close VB. Return to the C++ project. Make sure xmlWrapper.cpp is open and put a break on the first line below the beginning of the try block. To do this place the cursor over the line, right click, then choose insert/remove breakpoint from the popup window.

Go to the Build menu, choose Start Debug , and then choose Go (or alternatively simply press the F5 key). A dialog asking for the executable appears – put the full path to our VB program we just compiled. Alternatively, you can set the path to this executable on the Debug tab in Project | Settings . Click OK and then OK for the next dialog that appears. The code will begin executing and halt at your breakpoint.

Press F10 to step forward, F5 to go to the next breakpoint. You can now debug your ATL component. After the component makes it all the way through, you will see the VB executable appear with the desired XML. We have debugged the component – of course, you will want to test it again in your actual deployment environment – but this debugging saves you a lot of work.

Compiling for Prime Time

We are now ready to do the final compile. We want to minimize the DLL’s dependencies and add the C runtime libraries (CRT). If we do not set our project to minimize dependencies then we will need to ensure the proper version of ATL.dll is on our target platform. We also used the C runtime. To decrease the size of ATL DLL’s, the Visual C++ default settings do not include the CRT. We need to include them.

Select Set Active Configuration from the Build menu. From the dialog, select Win32 Release Min Dependency . Click OK . From the Project menu, select Settings . Choose the C++ tab and select C++ Language from the Category dropdown. Check the Enable exception handling checkbox. Go back to the Category dropdown and select General . At the end of the Preprocessor definitions textbox you should see _ATL_MIN_CRT ; delete it and click OK . From the Build menu choose Build XmlProject.dll and the project is compiled.

Caveats

Unlike VB, there are many ways to get yourself in trouble with Visual C++. In this tutorial, we have learned just enough to be dangerous. When programming VB, the methods and syntax are all straightforward. This simplicity is not so with C++. It seems like there are a million different ways to accomplish every little task. Some are efficient and some are not. It would behove you to befriend a C++ programmer and run your code by him or her on a regular basis. You should also buy Dr. Grimes’s book and the book he coauthored with George Reilly, Alex Stockton, and Julian Templeman.

You should also note that XML and XML parsers are changing very quickly. Other vendors, such as Oracle, have built in support for XML. Before deciding to implement any XML solution you would be well advised to go to your vendor’s website and search for any native support for XML they may have recently added to their product.

Conclusion

We created a general-purpose component to translate ADO to XML. By including a parameter as a path to an XSL template, our component can return any type of output an XSL style sheet is capable of producing. The source code for this component is included as well as a VB test program for you to download. You must have at least one ODBC connection on your computer. To compile as Unicode you must be running Windows NT or Windows2000. The Unicode will not run on Windows95/98.

When discussing ATL 2.0, George Reilly, a Microsoft Software Design Engineer, listed several performance advantages to C++ ATL components. Microsoft designed ATL to produce "small, fast, industrial strength components." ATL also "calls methods very quickly" and allows you a "fine control over COM features". As we have seen here, simple ATL components are also surprisingly easy to create. The next time you have to do something processor intensive, consider developing a C++ ATL component.

 
 
   
  RATE THIS ARTICLE
  Please rate this article (1-5). Was this article...
 
 
Useful? No Yes, Very
 
Innovative? No Yes, Very
 
Informative? No Yes, Very
 
Brief Reader Comments?
Your Name:
(Optional)
 
  USEFUL LINKS
  Related Tasks:
 
 
   
 
 
       
  Search the ASPToday Living Book   ASPToday Living Book
 
  Index Full Text Advanced 
 
 
       
  Index Entries in this Article
 
  • ADO
  •  
  • AppWizard
  •  
  • ATL components
  •  
  • C++
  •  
  • COM
  •  
  • compiling
  •  
  • debugging
  •  
  • design
  •  
  • DLLs
  •  
  • error handling
  •  
  • exception handling
  •  
  • implementing
  •  
  • import directive
  •  
  • ISupportErrorInfo interface
  •  
  • MSXML
  •  
  • Object Wizard
  •  
  • smart pointers
  •  
  • Standard Template Library
  •  
  • STL
  •  
  • String class
  •  
  • variables
  •  
  • Visual Basic
  •  
  • Visual C++
  •  
  • XML datasets, processing
  •  
     
     
    HOME | SITE MAP | INDEX | SEARCH | REFERENCE | FEEDBACK | ADVERTISE | SUBSCRIBE
    .NET Framework Components Data Access DNA 2000 E-commerce Performance
    Security Admin Site Design Scripting XML/Data Transfer Other Technologies

     
    ASPToday is brought to you by Wrox Press (http://www.wrox.com/). Please see our terms and conditions and privacy policy.
    ASPToday is optimised for Microsoft Internet Explorer 5 browsers.
    Please report any website problems to webmaster@asptoday.com. Copyright © 2001 Wrox Press. All Rights Reserved.