Custom Data Types Marshaling


This section discusses the marshaling of custom types through the ATL Server SOAP implementation for Web service clients . The mechanism used here requires you to modify the ATL Server source code, which Microsoft does not support. You should take this fact into consideration before you use this custom marshaling solution in an application.

We begin by looking at the problem: If a specific data type is frequently used in a Web service, it might be more convenient to the framework to support it natively rather than to wrap it for each call. For example, assume that a Web service uses structures that contain a field of type GUID . Let s assume further that the GUID type is defined in an XSD document as follows :

 <s:simpleType name="guid">    <s:restriction base="s:string">      <s:pattern value="[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-                                                 [0-9a-fA-F]{4}-[0-9a-fA-F]{12}" />    </s:restriction>  </s:simpleType> 
Caution  

We use the term guid (in lowercase letters) when we talk about the XSD type and the term GUID (in uppercase letters ) when we talk about the Windows Platform SDK GUID structure.

Thus, a GUID will be rendered and accepted by the server in the form of a string matching a specific regular expression. Sproxy.exe will render this type ( guid ) as a string (it s a restriction over a string, so it will be treated as a string). A structure containing a string and a guid will be represented in the proxy class as follows:

 struct someStruct  {    BSTR strVal;    BSTR guidVal;  }; 

To ensure the correctness of the data that the proxy class will receive and send, the client-side developer will have to create a wrapper for each function and check the value of the guidVal structure field. On the other hand, it s very likely that the client-side processing will need the guidVal in the GUID (Platform SDK) form to use it as a unique identifier. So, the wrappers should also convert the guid string to a real GUID . To preserve the consistency, the developer would also have to create some parallel structure that contains a GUID with the value contained in the guidVal field, and a string. Such a structure might look like this:

 struct someStructEx  {    BSTR strVal;    GUID guidVal;  }; 

This means that there s lots of code to write, and therefore lots of potential problems. A way of convincing the proxy class to work directly with the someStructEx type will save this effort.

Note  

An example with all the code described in this section is available as the customMarshaling sample. Only the client part and the WSDL are of interest at this point. The server part simply echoes back whatever the client is sending (i.e., it s only used to prove that this solution actually works).

Now that you ve defined the problem, let s see what kind of solution you can come up with. Let s start with the WSDL. Assume that it contains the guid type described previously and a structure containing a common string and a guid . Objects of this type (structures) are used as parameters in the methods described in the WSDL. So, besides the guid definition, the WSDL also contains the following (see GUIDService.wsdl):

 <s:complexType name="someStruct">    <s:sequence>      <s:element name="strVal" type="s:string"/>      <s:element name="guidVal" type="s1:guid"/>    </s:sequence>  </s:complexType> 

As you intend to modify the marshaling engine that resides in the atlsoap.h file that ships as a part of ATL Server, it s a good idea to make a copy of this file so that the original is available for other applications. An easy way to do this is to create a copy of atlsoap.h in the same directory as the sproxy.exe-generated file. To ensure that you re using the correct version, make sure to change the #include as follows:

 #include <atlsoap.h> 

to

 #include "atlsoap.h" 

In the marshaling engine, all the basic type elements are rendered through one of the AtlGenXMLValue < > template functions and are parsed by one of the AtlGetSAXValue < > template functions. As guid is a simple type (a string restriction), you ll want to provide a flavor of AtlGenXMLValue and AtlGetSAXValue that renders a GUID to a guid and reads a guid into a GUID , respectively.

The supported simple types are denoted internally in ATL Server by the SOAPTYPES enumeration in atlsoap.h. For future extensions, SOAPTYPES contains an element called SOAPTYPE_USERBASE , which is intended to be the last in the enumeration. Therefore, you can be sure that any number greater than SOAPTYPE_USERBASE won t generate a conflict with an existing SOAPTYPE .

For handling the GUID type, you ll define a new SOAPTYPE in the proxy-generated header file:

 #define SOAPTYPE_GUID SOAPTYPE_USERBASE + 1 

Now you ll need to modify the marshaling in atlsoap.h to support the newly added type.

The global AtlSoapGetElementValue function in atlsoap.h behaves as a dispatcher during the parsing of a SOAP payload. Based on the SOAPTYPE for a specific element, AtlSoapGetElementValue calls the appropriate AtlGetSAXValue implementation to parse the XML content representing that element and stores the parsing result in a variable.

The global AtlSoapGenElementValue function has a similar dispatcher behavior during the rendering of a payload. Based on the SOAPTYPE for a specific variable, it will render the XML content to represent it.

There are two other global helper functions to help deal with various SOAPTYPES : AtlSoapGetElementSize and AtlSoapCleanupElement . AtlSoapGetElementSize returns the size to be allocated for a variable holding a specific type (it s used when dynamic allocations occur ”for example, in the case of a variable size array), and AtlSoapCleanupElement makes sure that all the memory allocated by a variable of a specific type is released (e.g., the base64Binary elements are represented as ATLSOAP_BLOB structs, which contain buffers, so they have to be cleaned up after usage).

You must modify these four functions to support the new SOAPTYPE_GUID . You should keep in mind that SOAPTYPE_GUID is a definition local to the proxy-generated header file, so you ll have to use the value of this definition, which is SOAPTYPE_USERBASE+1 .

Note  

In the sample atlsoap.h file, all the modifications are preceded by the comment // Added for custom marshaling to help you identify all the modifications.

All these functions to be modified are acting as dispatchers, switching on a SOAPTYPES value to determine which function should be called. The modifications consist of adding a new branch to the switches (i.e., to the SOAPTYPE_USERBASE+1 value). For consistency, the new branch will be added right before the default branch.

First, based on the order in which the functions are implemented in atlsoap.h, we discuss the AtlSoapGetElementSize modifications. This function is supposed to return the size to be allocated for an element of a given SOAPTYPE . The newly added branch looks like this:

 case SOAPTYPE_USERBASE + 1:    nRet = sizeof(GUID);    break; 

So, when the SOAPTYPES parameter is SOAPTYPE_USERBASE+1 , the function will return the size of a GUID element (the internal representation of choice for a guid XSD type).

The next function to be modified is AtlSoapGetElementValue , the parsing dispatcher. The code to be added is as follows:

 case SOAPTYPE_USERBASE + 1:    hr = AtlGetSAXValue((GUID*)pVal, wsz, cch);    break; 

It s important to mention at this moment that the definition for AtlGetSAXValue looks like this:

 template <typename T>  inline HRESULT AtlGetSAXValue(T * pVal, const wchar_t * wsz, int  cch) 

This function is responsible for converting cch characters in the wsz buffer to a value of type T . The function is implemented for the basic types supported by ATL Server. The call to the function can deduce the template instance to be invoked based on the type of the pVal parameter. As you re casting pVal to a GUID* in the added code, the GUID template implementation will be invoked. Of course, such an implementation isn t available yet, as ATL Server doesn t support GUID , so you ll have to provide it later.

The next function to be modified is AtlSoapGenElementValue , the rendering dispatcher. The code to be added is as follows:

 case SOAPTYPE_USERBASE + 1:    hr = AtlGenXMLValue(pStream, (GUID*)pVal);;    break; 

At this moment we should mention that the definition for AtlGenXMLValue looks like this:

 template <typename T>  inline HRESULT AtlGenXMLValue(IWriteStream *pStream, T *pVal) 

This function is responsible for rendering a value of type T to the stream provided in pStream . The function is implemented for the basic types supported by ATL Server. The call to the function can deduce the template instance to be invoked based on the type of the pVal parameter. As you re casting pVal to a GUID* in the added code, the GUID template implementation will be invoked. Of course, such an implementation doesn t yet exist, so you ll have to provide it later, just as for AtlGetSAXValue .

The last function to be modified is AtlSoapCleanupElement . This function cleans any auxiliary memory needed to represent a value (such as the buffers for an ATLSOAP_BLOB struct). For the GUID type, no auxiliary allocated memory has to be freed, so the code to be added will use the main do-nothing path , as shown here:

 case SOAPTYPE_USERBASE + 1:    break; 

This concludes the necessary modifications in atlsoap.h. Now go back to the proxy-generated file. First off, you have to implement AtlGenXMLValue and AtlSoapGetSAXValue for GUID . The code in the sample is straightforward. AtlGenXMLValue attempts to create a string based on the content of a GUID , and then writes that string to the output stream:

 CString strFormattedGUID;  strFormattedGUID.Format("%08lX-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X",  pVal->Data1, pVal->Data2, pVal->Data3,      pVal->Data4[0], pVal->Data4[1], pVal->Data4[2], pVal->Data4[3],      pVal->Data4[4], pVal->Data4[5], pVal->Data4[6], pVal->Data4[7]);  return pStream->WriteStream(strFormattedGUID, -1, NULL); 

The format is the usual (readable) form used by both the Windows registry and the WSDL you started from.

AtlGetSAXValue uses the CRT function wcstoul to convert the hex numbers appearing in the XML content to their actual values. It contains multiple fragments as you can see here:

 CStringW  strWork;  wchar_t  *wszEnd;  strWork.SetString(pReadBuff, 8);  pVal->Data1 = (unsigned long)wcstoul(strWork, &wszEnd, 16);  if(*wszEnd != ' 
 CStringW strWork; wchar_t *wszEnd; strWork.SetString(pReadBuff, 8); pVal->Data1 = (unsigned long)wcstoul(strWork, &wszEnd, 16); if(*wszEnd != '\0'  errno == ERANGE) { return E_FAIL; } pReadBuff += 9; // (Data1 on 8 hex digits) + dash 
' errno == ERANGE) { return E_FAIL; } pReadBuff += 9; // (Data1 on 8 hex digits) + dash

Go back and look at the actual proxy class. It already contains the definition for the structure containing a guid , generated by sproxy as you can see here:

 struct someStruct  {    BSTR strVal;    BSTR guidVal;  }; 

As GUID is now a supported type, you should change the definition to

 struct someStruct  {    BSTR strVal;    /*BSTR*/GUID guidVal;  }; 

This kind of object is rendered and parsed based on a _soapmap structure generated by sproxy. The _soapmap structure contains higher level information about the object to be rendered, such as the container tag name ( someStruct ), the type of the object (in this case, SOAPMAP_STRUCT ) and some serialization flags. It also contains a reference to an array of _soapmapentry structures containing similar information about the fields of the object to be marshaled. You ll need to identify and modify the serialization information for the guidVal field.

The generic _soapmap structure that describes the serialization rules for a given object named SomeObject will be named __someObject_map by sproxy.exe. So, for the _soapmap associated with a someStruct object, you have to look for the definition of __someStruct_map in the proxy-generated file.

Once you find this definition, it s easy to identify the name of the array of entries describing the fields. Based on the declaration of the _soapmap structure (also, in atlsoap.h), the array of entries is the seventh field of a _soapmap . In the proxy-generated file you re working on, this field is __someStruct_entries .

You now need to locate __someStruct_entries in the header file. __someStruct_entries is an array of _soapmapentry structures. One structure describes the serialization rules for each field. You just have to identify the structure describing the serialization rules for guidVal . This is easy, as the first field of a _soapmapentry structure is the corresponding field to be serialized. The second entry is the one for guidVal .

 {  0x113E60CC,  "guidVal",  L"guidVal",  sizeof("guidVal")-1,  SOAPTYPE_STRING,  SOAPFLAG_FIELD  SOAPFLAG_NULLABLE,  offsetof(someStruct, guidVal),  NULL,  NULL,  -1  } 

The information here should remain unchanged, except for the fact that you don t want it to be serialized as a string anymore ( SOAPTYPE_STRING ), but as a GUID ( SOAPTYPE_USERBASE+1 or SOAPTYPE_GUID ). So, the modified entry for guidVal will look like this:

 {  0x113E60CC,  "guidVal",  L"guidVal",  sizeof("guidVal")-1,  /*SOAPTYPE_STRING*/SOAPTYPE_GUID,  SOAPFLAG_FIELD  SOAPFLAG_NULLABLE,  offsetof(someStruct, guidVal),  NULL,  NULL,  -1  } 

Now, almost everything is in place for using the GUID .

Atlsoap.h provides two template functions, allowing the client-side developer to clean up an object. These two functions are already implemented for the types supported by ATL Server ”you might want to implement them for the GUID type as well. In this particular case ( GUID ), they aren t required. GUID doesn t hold any allocated memory, so the cleanup won t do anything really useful, but ignoring these functions for more complex types can easily result in memory leaks. These two functions are

 template <typename T>  inline HRESULT AtlCleanupValue(T * /*pVal*/) 

and

 template <typename T>  inline HRESULT AtlCleanupValueEx(T *pVal, IAtlMemMgr *pMemMgr) 

These functions do the same thing (i.e., they clean the allocated memory), but AtlCleanupValue assumes that the memory was allocated with the CRT memory manager (e.g., malloc ), therefore it should be used for memory allocated by the client application (i.e., [in] parameters). The memory allocated through the ATL Server processing framework (e.g., variable size arrays transmitted through SOAP) comes from a memory manager object that, by default, is the CRT as well, but it can be overridden in order to use a private, customized heap. For memory allocated like this, the cleanup function must also know which memory manager was used in allocations. This is why AtlCleanupValueEx takes an IAtlMemMgr pointer. For example, when a proxy class returns a dynamically allocated array, that array should be cleaned with the memory manager of the proxy object, as follows:

 AtlCleanupValueEx(   object.., proxyObject.GetMemMgr()) 

But as we already pointed out, in the case of GUID there s nothing for these cleanup functions to do:

 inline HRESULT AtlCleanupValue<GUID>(GUID *pVal)  {  pVal;  return S_OK;  }  inline HRESULT AtlCleanupValueEx<GUID>(GUID *pVal, IAtlMemMgr *pMemMgr)  {  pVal;  pMemMgr;  return S_OK;  } 

Now that the proxy class fully supports GUID s, it s ready to use. The client application code will look something like this:

 CGuidService  service;  someStruct    stIn, stOut;  CoCreateGuid(&stIn.guidVal);  stIn.strVal = SysAllocString(L"TestVal");  HRESULT hRet = service.RetGuidSoap(stIn, &stOut);     processing here, using stOut.guidVal as a GUID 



ATL Server. High Performance C++ on. NET
Observing the User Experience: A Practitioners Guide to User Research
ISBN: B006Z372QQ
EAN: 2147483647
Year: 2002
Pages: 181

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net