Building XML Web Services for Windows Embedded CE 6.0

Back in 2002 I wrote an article for MSDN that talked about building XML Web Services for Windows CE .NET – The article discussed building an XML Web Service using native code, Windows CE doesn’t support managed code web services, so this means that you need to write your XML Web Services code in native code – this requires a combination of the SOAP toolkit, ATL, COM, the HTTPD Web Server, and XML HTTP, which are all supported as components in the CE 6.0 catalog. There’s no reason why you couldn’t write a pure managed code web server for Windows CE, and perhaps extend this to support Web Services, there are already examples of people having done this work.

Windows Embedded CE 6.0 is the current released version of the Windows CE product, the tools for building CE 6.0 operating systems are integrated into Visual Studio 2005 – this gives you one place to configure, build, download, debug and test your CE 6.0 based operating system, and also write your managed and native code applications.

The move to Visual Studio 2005 gives you the latest set of application frameworks with updates to ATL, MFC, and STL – A side effect of this change is that the steps to create the ATL/COM object from eMbedded Visual C++ no longer work – Let’s take a look at the steps needed to create the underlying operating system needed to support an XML Web Services component and also the steps needed to create the ATL/COM object and WSDL/WSML files which are needed to expose the web service.

There are a number of steps we will need to walk through.

  1. Create the baseline Windows CE operating system image (which includes adding support for a number of required components [COM, ATL, HTTPD, Soap Tooklit, XML HTTP]
  2. Create a custom Software Development Kit (SDK) for our operating system image
  3. Generate the ATL/COM object that exposes our custom interfaces
  4. Generate the WSDL/WSML that exposes our XML Web Service to other systems
  5. Get everything working!

For this example I’m going to base my operating system image on the ARM based device emulator, this means that you can follow along!

 

Step 1 – Create the baseline operating system image.

Hopefully I don’t need to explain how to create the starting point operating system image – I’m going to assume that you know how to use the Operating System wizard to generate a PDA device image for the emulator, and how to switch from a Debug image (default build) to a Release image.

// Pause here while you are configuring the baseline operating system image.

Ok – I will assume that you have your baseline image configured, and built, and that you have booted the image to prove that the operating system boots and runs…

Now let’s go ahead and add the components from the catalog that we need – there are two categories of components to add to the image, the first is ‘required components’, the second are ‘optional components’.

First the ‘required components’ – you can search the catalog for the SYSGEN names, this will jump to, and highlight the appropriate component in the catalog – you can also search for friendly names such as “.NET Compact Framework” – here’s the list of required SYSGENs to add to the platform.

  • SYSGEN_HTTPD (Web Server)
  • SYSGEN_ATL (Active Template Libraries)
  • SYSGEN_SOAPTK_SERVER (SOAP Server)
  • SYSGEN_MSXML_HTTP (XML/HTTP)
  • SYSGEN_CPP_EH_AND_RTTI (Exception Handling and Runtime Type Information)

Note: you will need to add the RTTI component to your image otherwise you will get build errors when you come to generate the ATL/COM object – in CE 5.0 this component was added automatically as a dependency of something else in the platform.

And now for the optional components – there are some helper components that I want to add to the operating system image that assist with things like getting the IP address of the target device and a wrapper component that gives me the Connection Manager pieces to make it easier to configure and connect from Visual Studio (application development) to my running embedded operating system image.

Here’s the ‘optional components’

  • SYSGEN_NETUTILS (ipconfig, ping etc…)
  • SYSGEN_CONMAN_ARMV4I (my own custom component that pulls in the Visual Studio Connection Manager pieces – I will pull this apart in a later post so you can see what the contents are – actually it’s really simple)

Now we need to build the operating system image with our new components added, we can then boot the image and get the IP address (we will need this later).

// Pause here while you add the required and optional components and build operating system image.

We’re going to be exposing an XML Web Service from the Device Emulator, this means that we will need to be able to ‘see’ the device emulator on the network – We need to configure the device emulator networking settings so that the emulator ‘piggybacks’ the networking card of our development PC – in Visual Studio 2005 select Target | Connectivity Options and then click the Network tab on the dialog – Since my laptop is connected on a hardwire Ethernet connection I selected my Ethernet adapter as the connected networking option (see below).

Networking_Properties

With networking configured I can now boot the Windows CE operating system image, get its IP address and then ping it from the desktop (since we also included the HTTPD Web Server in the operating system image we could also use the desktop web browser to browse to the default web page on the device).

There are a couple of options for getting the IP address from the device, the first is to double click the networking icon on the status bar of the Windows CE shell – this will display a dialog that shows the IP address and subnet – the second option is to use the Windows CE shell within Visual Studio 2005 – Assuming that you still have a connection to the device emulator, hit ALT+1 on your development machine keyboard – this will display the Windows CE command prompt – you can then type “s ipconfig” (Note: there are a number of commands you can issue from the command prompt, to get a list of available options type ‘?’ – The command ‘s’ means ‘start’ the following program, which in this case is ipconfig. The Debug output window will display the IP address of the device emulator – I prefer this approach since you can easily copy the IP address from debug output to the clipboard – this is useful because we will need this later (for configuring Connection Manager, and creating the WSDL for the device).

 

Step 2 – Create a custom Software Development Kit (SDK).

You are probably wondering why we need to do this step… easy, we’ve built a custom operating system image that contains a specific set of operating system features – if we’re going to be writing an application (or creating an ATL/COM object) then we will need to write this against the operating system features contained in our specific configuration of the o/s (the APIs are exposed through header files which will be put into the custom SDK).

Here’s the Solution workspace for my CE 6.0 project.

Solution_Explorer_1

Notice that there are a number of nodes to the solution – one of which is SDKs – if we right-click on the SDKs node we can create a custom SDK for our platform (use the “Add new SDK” option – I went with all the default options, filled in my name, company, and company web site URL) – if you right-click on the created SDK you can then build the SDK into an MSI installer file (useful for sharing with customers/partners).

Solution_Explorer_2 

Step 3 – Create the ATL/COM object.

If you have been through the process of creating an ATL/COM object using eMbedded Visual C++ 4.0 the following steps should be familiar to you, there are a couple of differences, and parsing the RGS/IDL to generate the WSDL/WSML files is also different – we will cover this later.

Let’s get started with the process of building the ATL/COM object – note that you will need to have created the underlying operating system and SDK, and installed the SDK on your development machine.

In Visual Studio 2005 (a second instance of VS 2005 if you still have the Platform Builder instance open) create a new Visual C++ project, since we’re building for Windows CE you will need to select a Smart Device project – and of course the type of project we’re creating is an ATL Smart Device Project.

New_ATL_Project

For this example I’ve given my project the name of CE6atlws (CE 6.0, ATL, Web Service), choose an appropriate name for your project, and then click OK.

The next step of the wizard is to choose the platform you are going to be building against, the default option is to build for Pocket PC 2003, you will want to remove that (use the “<” button) and select your custom SDK name (my platform SDK is called XMLWS [see below]).

SDK_1

SDK_2

Once you’ve completed the wizard you will have created a simple ATL project, you now need to add a Simple ATL Object to the project – this is where you will expose your XML Web Services code!.

In Visual Studio 2005, select Project | Add Class – Smart Device, ATL Simple Object – this will launch the ATL Simple Object Wizard – you will need to give your project a Short Name, this will automatically fill out the rest of the C++ and COM information.

Simple_object_0

Note that you will also need to change the threading model of the COM object to Free Threaded, you can either click on the “Options” link on the first page of the ATL Simple Object Wizard or click “Next” which will bring you to the same place.

BTW – if you want to know what the difference is between the different threading models (and why they exist) you might want to take a look at Larry Ostermans Blog post on the subject.

http://blogs.msdn.com/larryosterman/archive/2004/04/28/122240.aspx

Simple_Object

Just hit Finish to complete the wizard – ok, we’re done, the next step is to add our specific methods which will define the XML Web Services interface to our project.

Visual Studio provides a number of different ‘views’ on projects, you’ve probably already seen this with the Platform Builder project which gives you a solution view and a catalog items view of your operating system – in the ATL/COM object project we want to view the project as a set of classes and interfaces, not as a set of discrete source files – so, in Visual Studio, switch to the Class View.

Class_View

For this sample I’m going to expose a function (you could expose multiple functions) that returns the current memory load on the operating system, the function is going to be called GetMemoryLoad and is going to return a LONG value (actually, when you see the code you will notice that the function returns an HRESULT, the LONG return value is passed in as a parameter to the function call).

To add the function, Right Click on the ICE6WebService, and select “Add | Add Method”

Enter the Method Name as GetMemoryLoad, select the parameter type as LONG *, and make sure you mark the parameter as “out” and “retval” – finally, give the parameter a name, I chose m_dwMemoryLoad. (Simple!).

AddMethod

All you need to do now is add the code that implements the GetMemoryLoad function – Note that we added the method to the ICE6WebService (the interface), but we add the implementation of the function to the CCE6WebService class.

To add your implementation of the function, click on the “CCE6WebService” class in the Class View, this will show each of the exposed functions from the class (see below) – now double click on the GetMemoryLoad function – this will open the source file for editing.

CCEWebService

Here’s my implementation of the GetMemoryLoad function – The function actually does very little work, in this case we call the Win32 API GlobalMemoryStatus, which takes a MEMORYSTATUS structure as its input and return type, and return MEMORYSTATUS.dwMemoryLoad from the function.

STDMETHODIMP CCE6WebService::GetMemoryLoad(LONG* m_dwMemoryLoad)

{

MEMORYSTATUS memStatus;

memset(&memStatus,0x00,sizeof(memStatus));

memStatus.dwLength=sizeof(memStatus);

GlobalMemoryStatus(&memStatus);

*m_dwMemoryLoad=memStatus.dwMemoryLoad;

return S_OK;

}

That’s all there is to building the ATL/COM object! – Now onto the interesting part, generating the WSDL/WSML, deploying the ATL/COM object to the device, and consuming the Web Service from a desktop application – we’re nearly done!

 

Step 4 – Generate the WSDL/WSML file.

There are two ways to generate the WSDL/WSML files from your ATL/COM object, the first is to use the wsdlstb_ce.exe utility that ships with the CE 6.0 product (you can find the .exe here - \wince600\Public\COMMON\OAK\BIN\I386\wsdlstb_ce.exe\)

The command line utility works well, although requires a little detective work on your side to pull together the information needed for the wsdlstb_ce command line.

Here’s what you need to provide…

wsdlstb_ce -I <Exposed Class> -P <Class GUID> <ProgID> <Dll Name> <WebServer address and location of WSDL on the server> <name of WSDL file>

Given the project we’ve just created this would resolve to the following information.

wsdlstb_ce -I CE6WebService -P {19A1F07F-7659-4265-A830-5A13787A2963} ce6atlws.CE6WebService.1 ce6atlws.dll http://192.168.2.10/ce6atlws.wsdl ce6atlws.wsdl

All of the information we need to construct the command line can be pulled from the projects .RGS file – which in this case is CE6WebService.rgs – here’s the contents of the file, I’ve highlighted the parts we can rip straight from the .RGS file, this includes the Exposed Class, Class GUID, and ProgID – the name of the output DLL, the IP address of the target device, and the name of the WSDL are all straight forward.

HKCR

{

ce6atlws.CE6WebService.1 = s 'CE6WebService Class'

{

CLSID = s '{19A1F07F-7659-4265-A830-5A13787A2963}'

}

ce6atlws.CE6WebService = s 'CE6WebService Class'

{

CLSID = s '{19A1F07F-7659-4265-A830-5A13787A2963}'

CurVer = s 'ce6atlws.CE6WebService.1'

}

NoRemove CLSID

{

ForceRemove {19A1F07F-7659-4265-A830-5A13787A2963} = s 'CE6WebService Class'

{

ProgID = s 'ce6atlws.CE6WebService.1'

VersionIndependentProgID = s 'ce6atlws.CE6WebService'

ForceRemove 'Programmable'

InprocServer32 = s '%MODULE%'

{

val ThreadingModel = s 'Free'

}

val AppID = s '%APPID%'

'TypeLib' = s '{B501EF28-FDC9-4E8B-B1F8-C5C77C48E84F}'

}

}

}

The second method for generating the WSDL/WSML files is to use an updated version of my CEWSDLGen PowerToy utility for CE 6.0 (this is what I used to generate the WSDL/WSML for my project - this simply parses the RGS file, pulls out the bits it needs and auto-generates a CE 6.0 component that wraps the DLL, the COM object registration information [more on this shortly]) and the WSDL/WSML files.

I mentioned that my utility auto-generates the COM object registration information – if we were building a production device that uses XML Web Services we would want to boot the device and have all the COM objects pre-registered in the operating system image, there are two ways to achieve this – the first is to have a process run on boot that loads each COM object (LoadLibrary), calls GetProcAddress on DllRegisterServer, calls the DllRegisterServer function, and the unloads the DLL (FreeLibrary) – this seems to be an unnecessary step, wouldn’t it be better to have all of the COM objects pre-resistered on the operating system, so you just boot without needing to register anything?

Here’s how the .REG file looks for our ATL/COM object – all of the information needed to create this .REG file is ripped directly from the .IDL file for the project – the .REG file would typically be merged with the CE 6.0 operating system registry file so that the component registration is already part of the boot registry of the device.

REGEDIT4

;

; Copyright (c) 2004 Microsoft Corporation. All rights reserved.

;

; REG file created by CEWSDLGen

[HKEY_CLASSES_ROOT\CLSID\{19A1F07F-7659-4265-A830-5A13787A2963}]

@="CE6WebService Class"

[HKEY_CLASSES_ROOT\CLSID\{19A1F07F-7659-4265-A830-5A13787A2963}\InprocServer32]

@="\\Windows\\ce6atlws.dll"

"ThreadingModel"="Free"

[HKEY_CLASSES_ROOT\CLSID\{19A1F07F-7659-4265-A830-5A13787A2963}\ProgID]

@="ce6atlws.CE6WebService.1"

[HKEY_CLASSES_ROOT\CLSID\{19A1F07F-7659-4265-A830-5A13787A2963}\Programmable]

[HKEY_CLASSES_ROOT\CLSID\{19A1F07F-7659-4265-A830-5A13787A2963}\VersionIndependentProgID]

@="ce6atlws.CE6WebService"

[HKEY_CLASSES_ROOT\CLSID\{19A1F07F-7659-4265-A830-5A13787A2963}\TypeLib]

@="{B501EF28-FDC9-4E8B-B1F8-C5C77C48E84F}"

[HKEY_CLASSES_ROOT\TypeLib\{B501EF28-FDC9-4E8B-B1F8-C5C77C48E84F}]

[HKEY_CLASSES_ROOT\TypeLib\{B501EF28-FDC9-4E8B-B1F8-C5C77C48E84F}\1.0]

[HKEY_CLASSES_ROOT\TypeLib\{B501EF28-FDC9-4E8B-B1F8-C5C77C48E84F}\1.0\0]

@="\\Windows\\ce6atlws.dll"

[HKEY_CLASSES_ROOT\TypeLib\{B501EF28-FDC9-4E8B-B1F8-C5C77C48E84F}\1.0\0\Win32]

@="\\Windows\\ce6atlws.dll"

[HKEY_CLASSES_ROOT\ce6atlws.CE6WebService]

@="CE6WebService Class"

[HKEY_CLASSES_ROOT\ce6atlws.CE6WebService\CLSID]

@="{19A1F07F-7659-4265-A830-5A13787A2963}"

[HKEY_CLASSES_ROOT\ce6atlws.CE6WebService\CurVer]

@="ce6atlws.CE6WebService.1"

[HKEY_CLASSES_ROOT\ce6atlws.CE6WebService.1]

@="CE6WebService Class"

[HKEY_CLASSES_ROOT\ce6atlws.CE6WebService.1\CLSID]

@="{19A1F07F-7659-4265-A830-5A13787A2963}"

For now let’s concentrate on getting everything working – I can follow up with a discussion on how to get everything integrated into a complete operating system in a later post (let me know if that would be interesting ! - this blog post already feels long!).

 

Step 5 – Get everything working!

We’re nearly there! – Here’s what we have so far…

  1. CE 6.0 operating system containing everything needed to support XML Web Services
  2. ATL/COM object that exposes a function called GetMemoryLoad
  3. WSDL/WSML files that define the XML Web Service and the link into the COM interfaces for the exposed function.

We also have two instances of Visual Studio open, the first is our platform development tool, the second is our ATL/COM object development tool – now we need to pull everything together – this involves making a connection from the ATL/COM instance of Visual Studio to the Platform Development instance of Visual Studio – this is where Connection Manager comes in.

Take a look at the following article on MSDN that explains how to get Connection Manager working on your device - http://msdn2.microsoft.com/en-us/library/ms228708(VS.80).aspx – Note that the IP address referenced by this article is the IP address of your emulator image – I will write up the steps for integrating Connection Manager into a CE 6.0 image in another post.

With the connection made between your ATL/COM project and the Windows CE operating system you now have three minutes to deploy your ATL/COM DLL to the Windows CE device emulator – if you don’t deploy in the three minutes then you will have to re-make the connection between the ATL/COM instance of Visual Studio and the platform.

There’s a good chance that if you are following along with this post that you have built and downloaded a Debug build of the ATL/COM object – Ideally you would want to switch the build settings in Visual Studio to make this a release build. One side effect of building/deploying the ATL/COM object (Build | Deploy Solution) is that Visual Studio will register the COM object on the CE 6.0 device – this is great for testing, but you would really want to have the COM object pre-registered in the CE 6.0 image.

Now that the ATL/COM DLL has been downloaded and registered on the device we now need to ‘upload’ the WSDL and WSML files.

The WSDL file defines the exposed functions, parameters, and return types for our XML Web Service – the WSML file defines the linkage between the exposed function names and the dispatch ID of the COM interface.

To upload the WSDL/WSML files I’m going to use one of the Remote Tools from CE 6.0 Platform Builder, this is the Remote File Viewer (Target | Remote Tools | File Viewer) – this tool gives you the ability to upload files to a connected device, or pull files from a connected device to your desktop PC.

From Platform Builder, launch the Remote File Viewer (Target | Remote Tools | File Viewer) – you will be prompted to connect to a device – use the “Default Device”.

Here’s how the Remote File Viewer looks… I’ve expanded the folder structure on the left side of the application to expose the \Windows\www\wwwpub folder – this contains the file “Default.htm” the default page exposed from the HTTPD web server on the device – this is also the folder that we want to drop the WSML/WSDL files into.

Remote_File_Viewer_1 

To “upload” a file from your desktop to the Device Emulator use the File | Export menu item (Export really doesn’t seem like the right term to use here – I would prefer to see Upload/Download) – Browse to the folder you generated your WSDL/WSML files and ‘upload’ them to the \Windows\www\wwwpub folder on the device emulator (you can only upload one file at a time).

Here’s how the folder should look when you are done.

Remote_File_Viewer_2

Right! – Now onto the super easy part, writing the desktop C# application to consume the XML Web Service from our CE 6.0 Device Emulator.

Let’s start another instance of Visual Studio 2005, and generate a C# desktop forms based application, add a button to the form, and double click the button, this is where we will add our code to call the XML Web Service.

Since we’re going to be calling the XML Web Service we will want to add a Web Reference to the XML Web Service running on the device – select Project | Add Web Reference

Enter the appropriate IP address and name of your WSDL file, and then click Go

Web_Reference_1

If you’ve done everything right (and what could possibly go wrong!?) you will be rewarded with the Web Reference dialog looking something similar to the following – note that the WSDL has been parsed and the GetMemoryLoad function has been found and displayed (w00t!).

Web_Reference_2

The default Web reference name is “webreference” – I changed this to CE6WebService – to add the Web Reference to your application simply click Add Reference.

Here’s the Button Click code.

private void button1_Click(object sender, EventArgs e)

{

CE6WebService.ce6atlws Foo = new desktop_app.CE6WebService.ce6atlws();

int iMemoryLoad=Foo.GetMemoryLoad();

MessageBox.Show("Memory Load: "+iMemoryLoad.ToString());

}

Here’s the result of running the desktop application – click the button on the form, this calls the Web Service running on our CE 6.0 Device Emulator which then returns its current memory load.

c_sharp_application

We can confirm the current memory load of the actual device by running another remote tool from Platform Builder, in this case the Remote Performance Monitor.

Performance_Monitor

So there we have it… Almost everything you wanted to know about creating XML Web Services in native code to run on Windows Embedded CE 6.0 – there are still a couple of loose ends that need to be sorted out, this includes a breakdown of the ConMan component for CE 6.0, and integrating the ATL/COM object directly into the operating system image so that everything is ready to go at boot time.

Let me know if you have any questions!

- Mike