Home > Articles > Programming > C/C++

This chapter is from the book

Web Services in ATL Server

ATL Server does more than generate HTML: It generates XML as well. More specifically, you can use ATL Server to implement web services using the same ISAPI infrastructure it uses to generate HTML pages.

Whenever I look at a new web services stack (which has happened quite frequently in recent years), I like to start by building a simple service that will convert strings to upper- or lowercase, and that will return the length of a string. This lets me concentrate on the plumbing without worrying much about the implementation.

I started by creating a new ATL Server project. Visual Studio shows two icons on the New Project dialog box (see Figure 13.6) for ATL Server. I used the ATL Server project icon in the previous example. It turns out that the two project wizards are almost identical. The only difference between the two is the Create as Web Service check box on the Application Options page (see Figure 13.9). With the ATL Server Web Service project, this check box is on by default.

One other important difference exists. The marshalling code between XML and C++ is complicated and requires a lot of code to be generated. Instead of swamping your projects with generated code that's hard to edit and update, the ATL Server team built the code generation into ATL attributes. This means that, for all practical purposes, web services are the only part of ATL that requires the use of attributed code. When the Create as Web Service check box is set, the Developer Support page (see Figure 13.10) has the Attributed check box set and disabled. [7]

I created a project called StringLib for my new web service and got the expected ISAPI and Application projects, just like my previous one. The ISAPI project contained this code:

// StringLibIsapi.cpp : Defines the entry point for
// the DLL application.
#include "stdafx.h"
// For custom assert and trace handling with WebDbg.exe
#ifdef _DEBUG
CDebugReportHook g_ReportHook;
#endif
[ module(name="MyStringLib", type=dll) ];

   [ emitidl(restricted) ];

typedef CIsapiExtension<> ExtensionType;
// The ATL Server ISAPI extension
ExtensionType theExtension;
// Delegate ISAPI exports to theExtension
//
extern "C" DWORD WINAPI HttpExtensionProc(
  LPEXTENSION_CONTROL_BLOCK lpECB) {
  return theExtension.HttpExtensionProc(lpECB);
}

extern "C" BOOL WINAPI GetExtensionVersion(
  HSE_VERSION_INFO* pVer) {
  return theExtension.GetExtensionVersion(pVer);
}

extern "C" BOOL WINAPI TerminateExtension(DWORD dwFlags) {
  return theExtension.TerminateExtension(dwFlags);
}

The module attribute generates the type definition for the ATL module class. The emitidl attribute is used to prevent anything in that file after that point from going into the generated IDL file. The rest of the file is pretty much identical to the unattributed version. As with the HelloATLServer project, I didn't need to touch the ISAPI extension.

The interesting stuff ended up in the StringLib.h file in the application DLL project:

// StringLib.h
...
namespace StringLibService {
// all struct, enum, and typedefs for your web service
// should go inside the namespace

// IStringLibService - web service interface declaration
//
[ uuid("5CEAB050-F80B-4054-8E1B-43510E61B8CE"),
  
   object ]

   __interface IStringLibService {
  // HelloWorld is a sample ATL Server web service method.
  // It shows how to declare a web service method and
  // its in-parameters and out-parameters
  [id(1)] HRESULT HelloWorld([in] BSTR bstrInput,
    
   [out, retval] BSTR *bstrOutput);
  // TODO: Add additional web service methods here
};
  // StringLibService - web service implementation
  //
  [ request_handler(name="Default", sdl="GenStringLibWSDL"),
    
   soap_handler(
      
   name="StringLibService",
      
   namespace="urn:StringLibService",
      
   protocol="soap" ) ]
  class CStringLibService :
    public IStringLibService {
  public:
    // This is a sample web service method that shows how
    // to use the soap_method attribute to expose a method
    // as a web method
    [ soap_method ]
    HRESULT HelloWorld(/*[in]*/ BSTR bstrInput,
      /*[out, retval]*/ BSTR *bstrOutput) {
      CComBSTR bstrOut(L"Hello ");
      bstrOut += bstrInput;
      bstrOut += L"!";
      *bstrOutput = bstrOut.Detach();

      return S_OK;
    }
    // TODO: Add additional web service methods here
}; // class CStringLibService

} // namespace StringLibService

This default demonstrates quite well how you build web services with ATL Server. You start with an IDL interface and specify the inputs and outputs using various COM types. [8] Then, you create a class that implements that interface and add the request_handler and soap_handler attributes to tell the ATL Server plumbing that this is a web service implementation. Finally, you implement the interface methods, decorating each one with the soap_method attribute to wire up the incoming XML requests to the appropriate methods.

So, I removed the sample code, and created my interface:

[ uuid("5CEAB050-F80B-4054-8E1B-43510E61B8CE"),
  object ]
__interface IStringLibService {
  [id(1)] HRESULT ToUpper([in] BSTR bstrInput,
    [out, retval] BSTR *pbstrOutput);
  [id(2)] HRESULT ToLower([in] BSTR bstrInput,
    [out, retval] BSTR *pbstrOutput );
  [id(3)] HRESULT GetLength([in] BSTR bstrInput,
    [out, retval] long *pResult );
};

And the implementation class:

[ request_handler(name="Default", sdl="GenStringLibWSDL"),
  soap_handler(
    name="StringLibService",
    namespace="urn:StringLibService",
    protocol="soap"
  ) ]
class CStringLibService :
  public IStringLibService {
public:

  [soap_method]
  HRESULT ToUpper( BSTR bstrInput, BSTR *pbstrOutput ) {
    CComBSTR result( bstrInput );
    HRESULT hr = result.ToUpper( );
    if( FAILED( hr ) ) return hr;
    *pbstrOutput = result.Detach( );
    return S_OK;
  }

  [soap_method]
  HRESULT ToLower( BSTR bstrInput, BSTR *pbstrOutput ) {
    CComBSTR result( bstrInput );
    HRESULT hr = result.ToLower( );
    if( FAILED( hr ) ) return hr;
    *pbstrOutput = result.Detach( );
    return S_OK;
  }

  [soap_method]
  HRESULT GetLength( BSTR bstrInput, long *pResult ) {
    *pResult = ::SysStringLen( bstrInput );
    return S_OK;
  }
};

Aside from the attributes, this code wouldn't look out of place in any COM server implementation.

To use the web service in question, you need a WSDL file. ATL Server automatically generates the WSDL; it's accessible at http://localhost/StringLib/StringLib.dll?Handler=GenStringLibWSDL.

Consuming a Web Service in C++

After my web service was deployed, I wanted to test it. You can call most web services from almost every language. Because this is a C++ book, I created a C++ client. Luckily, Visual Studio includes a code generator that makes it fairly easy to make calls on a web service.

I started by creating a Win32 Console application in Visual Studio. To bring in the web service, I used the Add Web Service feature of Visual Studio to read the WSDL file from the web service and generate a proxy class [9] that wraps access to the web service. The rest of the C++ code simply collected some input and called the web service:

#include <iostream>
#include <string>
#include <atlconv.h>

using namespace std;

class CoInit {
public:
  CoInit( ) { ::CoInitialize( 0 ); }
  ~CoInit( ) { ::CoUninitialize( ); }
};

void _tmain(int argc, _TCHAR* argv[]) {
  CoInit coInit;

  cout << "Enter a string: ";
  string input;
  getline( cin, input );

  StringLibService::CStringLibService proxy;

  CComBSTR bstrInput( input.c_str( ) );
  CComBSTR bstrToLower;
  HRESULT hr = proxy.ToLower( bstrInput, &bstrToLower );
  if( FAILED( hr ) ) {
    cerr << "Call to ToLower failed with HRESULT " << hr;
    return -1;
  }
  cout << "This string in upper case is:" <<
    CW2A( bstrToLower ) << endl;

  CComBSTR bstrToUpper;
  hr = proxy.ToUpper( bstrInput, &bstrToUpper );
  if( FAILED( hr ) ) {
    cerr << "Call to ToUpper failed with HRESULT " << hr;
    return -1;
  }
  cout << "This string in lower case is:" <<
    CW2A( bstrToUpper ) << endl;

  int length;
  hr = proxy.GetLength( bstrInput, &length );
  if( FAILED( hr ) ) {
    cerr << "Call to GetLength failed with HRESULT " << hr;
    return -1;
  }
  cout << "This string is " << length <<
  " characters long." << endl;
}

The lines in bold show the calls to the web service. Calling via the proxy acts much like a call into a COM object: All calls return an HRESULT, the actual return value is given via an out parameter, and there are special rules for memory management (which are explained in the MSDN documentation). Because the web service client proxy uses the Microsoft XML 3.0 Server XMLHTTP object by default, I needed to initialize COM before making the calls.

This code simply calls all three of the methods in the web service. Figure 13.14 shows the result of running the test harness.

13atl14.jpg

Figure 13.14 Output from Web Service test client

SOAP Box: Why ATL Server Does Web Services Poorly

So, it's possible to implement web services—and do so in a way that's fairly comfortable to COM developers. What's not to like? A lot, as it turns out.

When using ATL Server Web Services, you have absolutely no control over the actual XML that gets sent out. ATL Server maps your COM interface into RPC-encoded SOAP; you can't hand it an XML schema. You can't even tweak the WSDL after the fact. You're stuck with what ATL Server gives you.

Now, perhaps you're thinking that this isn't too bad. Maybe you're defining a new service and don't need to conform to an existing XML schema. Unfortunately, you might be in for problem even then.

SOAP uses two basic styles to map the XML in the SOAP document to the underlying programming model. ATL Server uses the RPC encoding. This is a set of rules defined in the SOAP specification that say how an integer, string, array, and so on is represented in XML. This encoding does not use the XML Schema definition (although it does use the XSD type system) because the XSD specification wasn't finished when the SOAP spec shipped.

You would think that the RPC style would be sufficient, but the SOAP spec was sufficiently ambiguous that different vendors implemented RPC encoding in different and incompatible ways. With ATL Server adopting RPC encoding, you're in for interop trouble.

The other option is to use document-literal encoding. With doc literal, you simply treat the body of the SOAP document as XML. This turns out to be much more flexible and interoperable than RPC encoding. In fact, in the current SOAP 1.2 specification, [10] Section 5 (which defines RPC encoding) is completely optional. More toolkits are moving toward doing doc literal–style web services and are leaving out RPC encoding altogether. [11]

As a result, I simply can't recommend using ATL Server Web Services in any serious application. It might be useful if you have existing C++ code that has to be exposed as a web service now, now, now! But for anything that needs to interop with other environments, you're better off choosing a web service layer that supports more modern web services styles such as doc-literal and XML Schema.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020