Consuming a WCF service using a native WWS client

I have been meaning to play with Windows Web Services ever since I heard Nikola Dudar talk about it at the MVP Summit earlier this year. It’s natively included with Windows 7, but can also be installed and used from older OSes (XP, Vista, 2003 and 2008). You can write native clients using WWS that can connect to a WCF service, and similarly you can write a WWS native service that can be consumed by a WCF client. It’s so compatible that you can replace either a WCF client or a WCF service with a WWS equivalent without the other party being aware of it.

One of the first things I did was write a very simple WCF service and then connected to it using a simple WWS client. I intend to write a detailed article on that in the next few days but I’ll go through the basic steps in this blog so I can use that as some basic raw material for my article.

The first thing I did was to create a very simple WCF service. Here’s the service interface :

    [ServiceContract]
    interface IStringService
    {
        [OperationContract]
        string Reverse(string s);
    }

    class MyStringService : IStringService
    {
        public string Reverse(string s)
        {
            return new string(s.Reverse().ToArray());
        }
    }

Here’s some slightly stripped out code that shows how the service is created and run.

WSHttpBinding binding = new WSHttpBinding();

// .. set properties on binding

Uri baseAddress = new Uri("http://localhost:8000/StringService");

using (ServiceHost serviceHost = new ServiceHost(
  typeof(MyStringService), baseAddress))
{
    ServiceMetadataBehavior smb =
      serviceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
    if (smb == null)
        smb = new ServiceMetadataBehavior();

    // .. set properties on smb

    serviceHost.AddServiceEndpoint(
      ServiceMetadataBehavior.MexContractName,
      MetadataExchangeBindings.CreateMexHttpBinding(),
      "mex"
    );

    serviceHost.AddServiceEndpoint(
      typeof(IStringService), binding, baseAddress);
    serviceHost.Open();

Now there are two steps to do before we start writing the WWS native client :

  1. Use svcutil to generate WSDL from the WCF service
  2. Use the Windows WebServices compiler tool (wsutil) to generate proxies (c/h files).

Once you do this, create a native C++ console project, add the c/h files (and remember to disable precompiled headers for the C files). I am not going to show error handling code in the below code, but you need to check the HRESULT for error/success after every call. First thing is to declare some variables and also specify the service URL :

HRESULT hr = ERROR_SUCCESS;
WS_ERROR* error = NULL;
WS_HEAP* heap = NULL;
WS_SERVICE_PROXY* proxy = NULL;

WS_ENDPOINT_ADDRESS address = {};
WS_STRING url= WS_STRING_VALUE(L"http://localhost:8000/StringService");
address.url = url;

WS_STRING is a simple struct that has a WCHAR* (that will point to the string) and a ULONG that will represent the length. I’ve used the WS_STRING_VALUE macro to initialize the string there.

The WWS APIs provide rich error info through the WS_ERROR structure and so we create a WS_ERROR struct using the WsCreateError API call (using default arguments).

hr = WsCreateError(NULL,  0,  &error);
if (FAILED(hr))
{
  //...
}

We also need to create a WS_HEAP object which represents an opaque heap structure (error handling not shown) :

hr = WsCreateHeap(2048, 512, NULL, 0, &heap, error); 

The next step is to create the service proxy :

WS_HTTP_BINDING_TEMPLATE templ = {};
hr = WSHttpBinding_IStringService_CreateServiceProxy(&templ, NULL, 0, &proxy, error);

WSHttpBinding_IStringService_CreateServiceProxy is a proxy function that was generated by wsutil. Internally it calls the WWS API WsCreateServiceProxyFromTemplate but it saves us the hassle of correctly and properly filling up the various arguments. Now, we open the service proxy (connects us to the service endpoint) :

hr = WsOpenServiceProxy(proxy, &address, NULL, error);

At this point we are ready to make calls into the service for which we again use the proxy functions that are generated by wsutil.

WCHAR* result;

hr = WSHttpBinding_IStringService_Reverse(
        proxy, L"Nishant Sivakumar", &result,
        heap, NULL, 0, NULL, error);

if (FAILED(hr))
{
  // ...
}

wprintf(L"%s\n", result);

WSHttpBinding_IStringService_Reverse is generated for us and is a pretty simple function to use, and it internally wraps the call to the WsCall WWS API function, including correctly wrapping up all the arguments and the return value. Well, that’s pretty much it. Once you are done, just call all the close/free APIs :

if (proxy)
{
  WsCloseServiceProxy(proxy, NULL, NULL);
  WsFreeServiceProxy(proxy);
}

if (heap)
{
  WsFreeHeap(heap);
}

if (error)
{
  WsFreeError(error);
}

I was absolutely thrilled when I ran the console app and it successfully connected to the WCF service. The native client is about twice as long (number of lines of code) as an equivalent managed WCF client would have been but that’s a small price to pay for the ability to consume a WCF service in pure native code. In a later blog entry I will write about how the WCF service itself can be replaced with an identical WWS service (both WCF and WWS clients will continue to work the same).

Advertisements

4 thoughts on “Consuming a WCF service using a native WWS client

  1. Hello
    I would like to use Windows Web Services but I m unable to find the webservices.dll
    I installed Windows 7 SDK but still the same problem.
    So you know how i can get it? By installing a windows XP HotFix?
    Thanks

  2. Hi,

    Just curious.

    Why do you want to replace the WCF Service with the WWS Service?

    Is it because of better performance with WWS Service? Are there other reasons?

    Thanks,

    Kent

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s