WSDAPI 101 Part 5: Putting it all together to generate WS traffic

This is the fifth article in the WSDAPI 101 series. You'll learn the most by starting at the beginning, although it's not required to understand the content in this article.

Now that we know all about the pieces required in a device-oriented Web Services application, it's time to assemble all the pieces into a working application.  You can accomplish this with any number of commercial or free Web Services stacks, but the explanation today will focus on Windows' WSDAPI, since that's what I work on.

Step 1: Set up your environment
There are three things you'll need.

  • Visual Studio or another compiler.  This article will focus on VS2005 and VS2008.
  • The Windows SDK for the WSDAPI headers, libraries, and WsdCodeGen.  The Windows Server 2008 SDK is the most recent one released, and it will let you build 32-bit or 64-bit applications for Vista or Server 2008, both of which include WSDAPI.  You can use the new configuration tool to point your Visual Studio installation to the SDK.
  • A WSDL.  This one is a little tricky--you can either write one on your own, or you can find the WSDL for the application you're coding for.  We'll use the FileService sample WSDL included in the Windows SDK.

Step 2: Create a Visual Studio solution and projects
At this point, you should decide if you want to build a virtual device (and service), a client, or both.  The remainder of this guide assumes you'd like to build both--but if you're only interested in one, you can omit some steps to decrease your development time and decrease the total size of your code.

I typically build console applications (one each for the client and device), but nearly any C++ project is acceptable.

To use WSDAPI objects, simply #include <wsdapi.h> and link in wsdapi.lib, which contains some helper and factory functions.  WSDAPI.dll, which contains the bulk of the stack, will be loaded automatically at runtime.

Step 3: Generate some code
Use the WsdCodeGen tool (included in the SDK) to create the generated layer to customize the WSDAPI interfaces to your application.  We'll generate both client-side code and device-side code; the latter requires you to provide some information about the device's characteristics (below, in the second step).

  1. Generate a config file for WsdCodeGen.  In the same directory as FileService.wsdl, run "wsdcodegen.exe /generateconfig:all FileService.wsdl"
  2. Modify codegen.config to suit your application.  Look for a block of lines near the top of the config file that have values like, "### Sample Manufacturer ###"  Change these so your device will present a usable name.  Most of these fields are mandatory.
  3. Generate code.  Lastly, run "wsdcodegen.exe /generatecode codegen.config" to generate IDL, C++, and header files.

That's it!  All of the generated code is complete.  Add these files to your Visual Studio project.  You'll have to tweak the files somewhat to get them to compile (WsdCodeGen was originally intended for the build environment in the Windows Driver Kit).  This will be fixed in the future.

Step 4: Create a service
Steps 4 and 5 are only necessary if you want to build a virtual device and host a service.  If you're not interested, you can skip to step 6.

Open the IDL file and inspect the interfaces inside.  The base interface is the one you must implement--in the case of FileService.wsdl, it's called FileService, and it only inherits from IUnknown.  You must properly implement IUnknown in your service class, and must at least implement empty stubs for all service methods.

Step 5: Create a device host for your service
The service must live inside a container--in WSDAPI, this container is the IWSDDeviceHost.  Both the FileService and StockQuote samples demonstrate how to create and configure a host.  The important steps are:

  1. Create an XML context with WSDXMLCreateContext
  2. Register your XML names with this context by calling FileServiceRegisterNamespaces
  3. Create a device host with WSDCreateDeviceHost.  You'll need to choose an ID for your host--remember this for step 6.
  4. Set metadata by supplying two generated structures ("this model metadata" and "relationship metadata") and one hand-built structure ("this device metadata") to the SetMetadata method.
  5. Register the FileServicePortType with RegisterPortType
  6. Instantiate your service object and register it with RegisterService
  7. Start the host with ... Start!

Of course, always remember to Stop and Terminate the host when finished, and always remember to clean up variables and handle errors. 

It looks like a lot of work, but the pattern is the same for every host.  The device host emphasizes flexibility over simplicity, so simple applications like FileService are often a little more work than necessary, but these apps can grow easily with the extended support in the WSDAPI objects.

Step 6: Create a client
Thankfully, this is simpler than creating a device.

  1. Create a device proxy with WSDCreateDeviceProxy.  Specify the device ID you chose in the previous step.  Searching for devices available on the network is an advanced topic, and will be covered in another article.
  2. Get a service proxy by calling GetServiceProxyByType
  3. Create a FileServiceProxy and initialize it with the service proxy in the previous step
  4. Send messages (e.g., GetFileList)

Step 7: Compile and run
That's it!  Simply build the code, fire up the device, and then run the client program.

At first glance, this article looks pretty intimidating.  There are certainly a lot of steps--some of which are fairly involved.  But once you understand the pattern of generating code, building an app, and compiling it all together, authoring complex network applications that can communicate with actual devices becomes really straightforward.  Sure, it may be easier to write a simple socket program to do what the StockQuote or FileService samples demonstrate, but the pattern above can be applied to a WSDL of any complexity and the effort required is the same.  Letting Web Services handle the messaging allows you to focus on the logic above the messaging and, more importantly, the overall solution that the application presents.

I hope you've enjoyed WSDAPI 101.  I'll continue to touch on basic concepts for the lifetime of this journal, but I'm hoping that this guide has served to illustrate the architecture and steps behind Web Services applications written for devices.  If you've got questions that weren't answered here, please feel free to post a comment.

Comments

  • Anonymous
    April 06, 2008
    Hi Dan, I am a driver/imageprocessing developer and new to Client Server technologies... may be my issue is quite silly. Step 3: Task 3 -> Generate Code I followed your instructions and had a problem in the above step. FileService.h file is not generated, it leads to build failure. Ofcourse I copied the file from FileService project. Is it done intentionally??? Thought of letting you aware... Thanks and Regards, Garysh

  • Anonymous
    April 10, 2008
    Hi Garysh- Visual Studio instructs MIDL to generate "FileService_h.h" instead of "FileService.h".  To fix this problem:

  • Right click "FileService.idl" in the Solution Explorer, open Properties

  • Navigate to MIDL, Output

  • Change the Configuration dropdown to "All Configurations"

  • Change the "Header File" field from "$(InputName)_h.h" to "$(InputName).h" --D

  • Anonymous
    April 27, 2008
    Hi Dan, I am working on development of WIA scanner driver. I have generated the code for scan service. The communication using WSDAPI is successful. I want to know how can I know the detail of the error, when some call through generated code fails. ex. if CreateScanJob fails it is returning E_FAIL always from generated code. How can I know the error details like if device is busy etc. I have checked with microsoft's inbox scan driver. On error the trace is showing, a method GetOperationFault being called. Can you provide information what exactly it is doing? Thanks in advance, Nitin.

  • Anonymous
    April 28, 2008
    Hi Nitin- The best way to inspect failures occuring through WSDAPI is to check the return code from the failing method.  We currently have no way for you to extract detailed information from inside WSDAPI. If the calls are failing inside generated code, you can manually debug those as if they were hand-written source. Inspecting the wire traffic using a network sniffer (e.g., Netmon or Wireshark) will allow you to see if the device is responding to the initial request. Best of luck --D

  • Anonymous
    April 29, 2008
    Hi Dan, Thanks a lot for instant reply. I am using the same way for debugging and tools. As you have told, currently WSDAPI not supporting getting details of error. I have checked the traces from MS inbox scan driver. If some generated code call fails, like EndRetrieveImage, it is calling some method GetOperationFault. The traces also show some information about the error. I just wanted to know how is it able to give those information. FYI I am including some traces. ============================================= WIA: 2264.3324 1161141 0 0 [wiaservc.dll] wiasDebugTrace, WSDScDrv: IScanService::EndRetrieveImage failed (hr = 0x80004005), executing IScanService::GetOperationFault.. WIA: 2264.3324 1161141 0 0 [wiaservc.dll] wiasDebugTrace, WSDScDrv: CWSDDevice::GetOperationFault.. WIA: 2264.3324 1161141 0 0 [wiaservc.dll] wiasDebugTrace, WSDScDrv: Executing IScanService::GetOperationFault.. WIA: 2264.3324 1161141 0 0 [wiaservc.dll] wiasDebugTrace, WSDScDrv: IScanService::GetOperationFault returned fault code: Sender, subcode: ClientErrorNoImagesAvailable, reason: The server has no images available to aquire. WIA: 2264.3324 1161141 0 0 [wiaservc.dll] wiasDebugTrace, WSDScDrv: IScanService::GetOperationFault indicates no data (WIA_ERROR_PAPER_EMPTY): ClientErrorNoImagesAvailable WIA: 2264.3324 1161141 0 0 [wiaservc.dll] wiasDebugTrace, WSDScDrv: IScanService::EndRetrieveImage returned no data, hr = 0x80210003 ================================================== I wanted to know what GetOperationFault is doing. How is it able to get those details of error ? Thanks, Nitin.

  • Anonymous
    April 30, 2008
    The comment has been removed

  • Anonymous
    May 29, 2008
    hi Dan, I am trying to build a scanner driver for a device. I have the wsdl file of the service. I am successfully generating the codegen.config from the wsdl file but when i am trying to generate the proxy, stub and various other file it is showing the following error "WsdCodeGen.exe : ERROR :  is not a valid URL" Can you help me in this Thanks, Saurabh

  • Anonymous
    October 18, 2008
    Hi , I am trying to develop new webservices for winodws CE network device. I didt get , which wizard i need to use to create new webservices for winodws CE device using WSDAPI. How to create new WSDL files for new service . REgards, Sush

  • Anonymous
    October 21, 2008
    Hello Sush- There is no wizard to create a service for WSDAPI.  The steps are fairly straightforward, and are explained in the documentation for our samples (http://msdn.microsoft.com/en-us/library/bb872383(VS.85).aspx). The WSDL files are distributed by whoever writes them.  If you are using the WSD-Print or WSD-Scan WSDL (for example), you can find them on the Microsoft website.  If you are using a WSDL from another agency, you will have to download the WSDL from them.  You may write your own WSDL as well, but you will be the only implementer until you distribute the WSDL to other software developers. --Dan

  • Anonymous
    November 19, 2008
    Hi Dan. When i test  fileservice sample on my device, get operation of client side is not working in device. How can i debug that in my device. Rama.

  • Anonymous
    November 20, 2008
    Hi Rama- I don't follow--can you explain the setup and what exactly is failing?  Is the client failing to generate a message, or is it failing inside the device? Thanks --D

  • Anonymous
    May 05, 2009
    Hi Dan, You have explained how the client can communicate with the device if we know its device id. Can you please let me know how can I search for devices available on the network and then communicate with them? Thanks Raveesh

  • Anonymous
    May 08, 2009
    Hi Raveesh- Take a look at scenario 3 in this post: http://blogs.msdn.com/dandris/archive/2008/04/28/what-s-the-deal-with-wsdapi-s-ws-discovery-interfaces.aspx The short version is that you will want to use Function Discovery to search for devices.  Function Discovery allows you to specify constraints to limit your query (e.g., "only search for printers"). --D

  • Anonymous
    May 12, 2009
    Thanks Dan. I found a sample program on Function Discovery, in Windows SDK, in the following folder - <Windows SDK folder>SamplesNetDsFunctionDiscovery.

  • Anonymous
    May 12, 2009
    Hi Raveesh- Yep, that's correct.  The PnP-X samples also show Function Discovery at work. I believe both the Function Discovery and the PnP-X samples will be moving to another directory in the SDK in more recent versions.  If you cannot find them under the "NetDs" directory you may want to look elsewhere in the SDK. --D

  • Anonymous
    February 05, 2010
    Hi Dan, I am testing a print device/service, for which I have generated the client code using wsdcodegen on the printservice.wsdl. As I know the UUID of the device, I pass the device UUID to WSDCreateDeviceProxy. hr = WSDCreateDeviceProxy(Device UUID, client UUID, pContext,ppDeviceProxy); Where pContext has been set by the generated code with namespace "http://schemas.microsoft.com/windows/2006/08/wdp/print" But WSDCreateDeviceProxy returns an "unspecified error". Although in the network trace I get to see ResolveMatch and GetResponse from the device. Could you please let me know what the problem might be? Thanks and regards, Smita

  • Anonymous
    February 05, 2010
    Hi Smita- If you see both the Resolve and Get messages but device proxy creation fails, it is likely that WSDAPI is rejecting the metadata contained in the GetResponse message. Check the GetResponse message against known-good samples, such as those on MSDN:  http://msdn.microsoft.com/en-us/library/ee886311(VS.85).aspx --Dan

  • Anonymous
    February 07, 2010
    Hi Dan, Thanks for the reply. I have tried this WSD client with a printer which is Windows Logo certified, but even with that device I get an error when I call WSDCreateDeviceProxy. One observation in the network traces is that even though the WSDCreateDeviceProxy call fails, after sometime of calling WSDCreateDeviceProxy I get to see GetPrinterElementRequest (even though there are no other WSD clients running in the PC). Thanks and regards, Smita

  • Anonymous
    September 07, 2011
    Hi Dan, I am testing the Stockquote sample provided by MS on Windows compact 7 device. The stockquote service starts properly but when I try to run the stockquote client on the same device it fails to create proxy, but when I unplug the network cable from device client just works fine. I also have observed one thing when running the Stockquote sample on windows 7 desktop. if I disable IPv6 from Local Area Connection then only clients from different machine work properly otherwise they fail to create proxy. Is this an issue with WSDAPI? Thanks Rahul.

  • Anonymous
    September 08, 2011
    Hi Rahul- I'm not sure why you are encountering this behavior. I suggest you contact Microsoft developer support. Thanks --D

  • Anonymous
    September 27, 2011
    Hi Dan, I am experiencing issues while creating a device proxy for secure WSD service. Service is hosted on windows CE 7 device on https (https://devicename:5358/), the certificates were generated using makecert and are working fine when used with HTTPD. I've used HttpSetServerConfiguration() API to bind certificate to port 5358. when I run client from windows 7 machine device proxy creation times out. when I debug client and step through code, then some times the proxy is created and GetLastTradePrice() executes properly. As this works some times while debugging it rules out possibility of bad certificates or any other certificte related issues. what could be the reason behind the time out error when I directly run the client? I can see client see Client hello, server hello , client key exchange etc handshake messeges in network monitor in the fail case as well.

  • Anonymous
    October 18, 2013
    Hi Dan, This may sound a little silly, but how can I create my own wsdl for a certain device. What we want to do is to use a projector wirelessly but I dont have nay idea of how.

  • Anonymous
    October 01, 2014
    Is it possible to customize content-type of attachment at run time?