Erwin's blog

Hosting webservices on Windows Embedded Compact gSOAP Part II (WEC2013)

Introduction

Back in 2011 I wrote an article about hosting webservices on Windows Embedded Compact with gSoap. I recently got some comments on this article and I am happy to notice that this subject is still relevant. The article written back in 2011 was targeted for Windows CE 6.0, and so I decided to redo the article for Windows Embedded Compact 7 (WEC7) and Windows Embedded Compact 2013 (WEC2013) including the latest version of gSOAP.

In fact for WEC7 you can just follow the original article as the environment (VS2008) has not changed. Due to an update of gSoap a modification is needed in order to make things work again. This modification, stubbing the time() function, is also needed for WEC2013 and therefore this article is mainly focused on WEC2013. A note is given in this article which part is relevant for WEC7.

The sample code provided has been build and tested with our iMX6 SDK, and the target used is the SABRE-Lite board. You can download the bootloader, the kernel (nk.bin or nklz4.bin) and the SDK for all of our supported platforms here.

You can find the source code attached to this article for both WEC7 and WEC2013. In order to use the attached source code you will need to install the GuruCE i.MX6 SDK and download the gSoap source code.

As said this article is based on the hosting webservices on Windows Embedded Compact with gSoap article and you can find the complete source listing in the original article. This article assumes that you at least have read the article

Windows Embedded Compact 2013

A lot has changed with WEC2013 and therefore I will discus the steps of how to (re)create the HelloWorldWebService application for WEC2013. The HelloWorldWebService application is a web service running on an Windows Embedded Compact 2013 device. We can reuse the desktop client application created in the previous article as long as the wsdl did not change. If the wsdl file has changed you need to open the desktop client solution and update the service reference in the solution explorer. This article assumes that the wsdl file has not changed and this thus means that you can use the desktop client application without any modification or rebuilding!

For this article we will use version 2.8.44 of gSoap which is the latest version at the time of writing this article. gSoap has been modified which causes the sample code, listed in the previous article, not to build anymore. We will explain how to fix the compile error in this article. With WEC2013 the development environment is Visual Studio 2012/2013/2015 which requires some modifications needed as well.

Create the native c++ gSoap webservice application for WEC2013

As a reminder; the steps to follow:

  1. Import the WSDL in a new project
  2. Let gSOAP generate a header file via wsdl2h.exe
  3. Let gSOAP generate the corresponding implementation *.cpp files via soapcpp2.exe
  4. Implement our methods

Step 1: Import the WSDL in new project

Open Visual Studio 2012/2013/2015 and create a new C++ smart device project (File | New | Project) and enter HelloWorldWebService as the name of the solution. In the "New Project" Dialog navigate to the Template "Templates | Visual c++ | Windows Embedded Compact | GuruCE iMX6 SDK" and select the "Win32 Console Application".

When you click OK the project is being created. There is no longer an option to select an empty project and thus you will get stuff for free here. This means we need to modify the project we just created, in order to reuse the same sources files which we used in the previous article. We also want to be able to use the same source files for WEC2013 and WEC7.

  1. First delete all source and header files which were automatically generated by Visual Studio's "new project wizard". e.g. right click on stdafx.h and choose remove | delete.
  2. Add the gSoap filters for the header and sources files (right click on header files and choose "Add | New Filter" and choose "gsoap" for the name of the filter.
  3. Create a new file called HelloWsdl.wsdl, copy the wsdl contents from here and import the file into the solution.

The layout of the solution should be looking like this:

Step 2: Let gSOAP generate the header file from our wsdl.

First download gSOAP in order to get the tools needed. You can download gSOAP from here.

Make sure you save the extracted files in an appropriate folder for you to remember because we need references to this folder within our visual studio project later on. I’ve put the gSOAP tools in the $(SolutionDir)

In the solution explorer in Visual Studio 2015 right click on the just imported wsdl file and select properties.

Select "All Configurations" for the configuration.

In the “Custom Build Tool“ pane, add the following command (make sure you get the path right and that the solution directory doesn't contain any spaces) in the “Command Line” section:

$(SolutionDir)gsoap-2.8\gsoap\bin\win32\wsdl2h.exe -s %(Filename).wsdl -o $(ProjectDir)%(Filename).h

!The macros in Visual Studio 2015 have changed compared to Visual Studio 2008!

This will run the wsdl2h.exe to generate our header file. The parameters specify the input and output file. In the “Outputs” field on the property page enter: $(FileName).h


Click “Apply” and “OK”, and then right click the wsdl file again and choose “Compile”.
Now our header (HelloWsdl.h) file is generated in the project directory, and we need to add this file to the project. In the solution explorer right click on the “gsoap” folder in the header section and choose “add existing item”. Navigate to HelloWsdl.h and add it.

Let’s do the same for generating the cpp file:

Step 2: Let gSOAP generate the cpp source file.

In the solution explorer right click on the HelloWsdl.h, that we just added in the previous step, and select “Properties”. In the “Custom build Tool“ add the following....wait, where is the "Custom Build Tool" configuration?

*** This is a BUG in Visual Studio 2015 ***

Press "Cancel" or "OK" to close the dialog. Yikes...The workaround is to manually edit the vxproj file (eg. with notepad) to add our custom build action. First save the solution!. After saving the solution edit HelloWorldWebService.vcxproj and look for the tag <ClInclude Include="HelloWsdl.h" />.

Replace:

<ItemGroup>
    <ClInclude Include="HelloWsdl.h" />
</ItemGroup>

Into

<ItemGroup>
    <CustomBuild Include="HelloWsdl.h">
      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|GuruCE iMX6 SDK'">$(SolutionDir)gsoap-2.8\gsoap\bin\win32\soapcpp2.exe  -S $(MSBuildProjectDir)%(FileName).h</Command>
      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|GuruCE iMX6 SDK'">$(FileName).cpp</Outputs>
      <Command Condition="'$(Configuration)|$(Platform)'=='Release|GuruCE iMX6 SDK'">$(SolutionDir)gsoap-2.8\gsoap\bin\win32\soapcpp2.exe  -S $(MSBuildProjectDir)%(FileName).h</Command>
      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|GuruCE iMX6 SDK'">$(FileName).cpp</Outputs>
    </CustomBuild>
  </ItemGroup>

Save the file and go back to Visual Studio. You will notice that Visual Studio detected that the project has been changed. Visual Studio will ask if you would like to reload the project and yes we do: Choose "Reload All":

In the solution explorer right click again on the HelloWsdl.h, and you will see that the "Custom Build Tool" step has been recovered:


Make sure that the paths are correct! You can see that we are using different macros than we used in Visual Studio 2008. Right click on the HelloWsdl.h file and choose "compile". This will generate a bunch of files as well, but we are not yet ready to build our solution.

Right click the gSOAP folder in the header section in your solution and add these header files:
- soapH.h
- soapStub.h

Right click the gSOAP folder in the source section in your solution and add these cpp files:
- soapC.cpp
- soapServer.cpp

The next step is to add stdsoap2.h and stdsoap2.cpp to your solution. You can find these two files in “gsoap-2.8\gsoap”. Add them together by right clicking the project in the solution explorer and select “Add existing item”. They will automatically appear under the correct “header” and “source” sections.

We’ve added the stdsoap2.h to our solution but we also need to add the directory where stdsoap2.h resides to the project's "include directory list". Add “$(SolutionDir)\gsoap-2.8\gsoap” to the include directory list by right clicking the project in the solution explorer and click "Properties". In the “Configuration Properties" | C/C++ | General” section you will find “Additional Include Directories”.

Now that we’re done generating files we can actually start to code!

Step 4: Implement our methods

Just like in the original article we need to create the source files HelloWsdl.cpp and HelloWsdlMethods.cpp to the solution. Copy or reuse the files HelloWsdl.cpp and HelloWsdlMethods.cpp used in the original article that you can find here! The content of those could be left unmodified!

Next step is to make sure that we configure the project not to use "Precompiled headers" because we deleted all those files in the beginning. We also want to be compatible with the WEC7 solution and thus we want to use the same source files. So right click on the solution explorer and navigate to the "Configuration Properties | C/C++ Properties | Precompiled headers". Choose "Not using Precompiled Headers "


For the same reason we need to add the UNICODE defines to the preprocessor definitions. Navigate to the "Configuration Properties | C/C++ Properties | Preprocessor". Add the UNICODE and _UNICODE definitions to "Preprocessor Definitions"


Now build the solution.... It will fail with the following compile error:

..\gsoap-2.8\gsoap\stdsoap2.cpp(16942): error C3861: 'time': identifier not found

This same compile error will occur when you follow the original article due to the fact that the gSoap sources are changed. For WEC7 you will only have to follow the following steps to make the solution working again

Now when you take a look at the specified line in stdsoap2.cpp the function time() is begin called which is not supported on Windows Embedded Compact. We need to stub/implement this method in Embedded Compact. This require two steps. The first step is to modify stdsoap2.cpp and tell that time() is external. Unfortunately this means that we need to modify the gSoap code and so whenever you update the gsoap package you will need to redo this step.

Open stdsoap2.cpp and add the following snippet just prior the function soap_rand_uuid where the time() function is used.

#ifdef WINCE
extern time_t time(time_t* timer);
#endif WINCE

The second step is to create the time() implementation. Add a new source code (cpp file) called "time.cpp"(right click on Source Files in the solution explorer and select: Add | New Item and choose c++ file(.cpp)). The time.cpp will hold the stubbed time function. You could proper implement the time() function, for example the OpenNETCF time replacement for CE can be found here, but as the method indicates that all it requires is a random uuid and for the sake of the simplicity of this article we will stub this function as follows:

#include <windows.h>

extern "C" time_t time(time_t* timer)
{
    return(time_t)rand();
}

Copy and paste the above code snippet in time.cpp and the complete project should look like:

As said the desktop client application should just work "AS-IS" as long as the wsdl hasn't changed.

That's it, build, deploy and enjoy!

Steps to follow to build the attached solution(s)

  1. Download the Webservice sample code and extract it somewhere suitable (In my case: H:\Data\development).
  2. Download and extract gSOAP into the $(SolutionDir) (So the gsoap-2.8 folder resides in H:\Data\Development\HelloWorldWebService).
  3. Open HelloWorldWebService7.sln or HelloWorldWebService8.sln depending on your environment.
  4. Modify stdsoap2.cpp to extern the time() function as described in this article.
  5. Build the solution.

Folder view:

Platform-specific macros in WEC2013 SDKs

As you can see in the WEC2013 release notes, the "Platform-specific Macro" in the SDK properties is no longer supported.
This is a change from WEC7 where you could simply add platform specific macros in the SDK's options dialog:

CE7 SDK options dialog

This way you can add custom preprocessor definitions that will be set by default for all your applications developed against your SDK. So, in the picture above, the preprocessor definition BSP_REV=9999 and __RPC_ARM32__ are set (on top of some definitions set by default). The impact of adding the above to the platform-specific macro option is that when you create a (new) smart device project against the SDK built with those platform specific macros set, the compiler options will look like this:

Test application compiler options

Adding these platform-specific macros can be very useful but unfortunately WEC2013 lost the ability to easily set these "platform specific macros". In WEC7 the platform-specific macros are stored in the SDK configuration file, but even adding the

<Property NAME="PlatformFamilyDefine">BSP_REV=9999;__RPC_ARM32__</Property>

tag to the SDK's configuration file (*.sdkcfg) doesn't work in WEC2013. Luckily, there is a possibility to achieve the same in WEC2013, but it is really unnecessarily complicated. We have no idea why Microsoft killed this SDK feature in WEC2013 and made it so difficult...

The build procedure for building an SDK is different for WEC2013 and WEC7. In WEC2013 the SDK preprocessor definitions are stored in a file named microsoft.cpp.sdk_name.ce800.props, located in:

<WINCEROOT>\public\COMMON\sdk\msbuild\[arch]\SDK_NAME\PlatformToolsets\CE800

[arch] is 'arm' or 'x86' depending on the SDK target architecture.

As you know, you should never modify any file in the PUBLIC (and PRIVATE) trees, but luckily this file is being copied during the sysgen phase and thus we can intercept and modify it before building the SDK.

To add custom pre-processor definitions to the WEC2013 SDK you need to modify the sysgenned and copied file microsoft.cpp.sdk_name.ce800.props in:

%SG_OUTPUT_ROOT%\misc\sdk\msbuild\[arch]\SDK_NAME\PlatformToolsets\CE800\microsoft.cpp.sdk_name.ce800.props

Here's a snippet from microsoft.cpp.sdk_name.ce800.props with our modifications:

<ItemDefinitionGroup>
    <ClCompile>
      <PreprocessorDefinitions>UNDER_CE;WINCE;_WIN32_WCE=0x800;ARM;_ARM_;_USESTL;BSP_REV=9999;__RPC_ARM32__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <BasicRuntimeChecks></BasicRuntimeChecks>
      <SDLCheck></SDLCheck>
      <AdditionalOptions>/QRunaligned- %(AdditionalOptions)</AdditionalOptions>
    </ClCompile>
    <ResourceCompile>
      <PreprocessorDefinitions>UNDER_CE;WINCE;_WIN32_WCE=0x800;ARM;_ARM_;_USESTL;BSP_REV=9999;__RPC_ARM32__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ResourceCompile>
    <Midl>
      <PreprocessorDefinitions>UNDER_CE;WINCE;_WIN32_WCE=0x800;ARM;_ARM_;_USESTL;BSP_REV=9999;__RPC_ARM32__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </Midl>
  </ItemDefinitionGroup>

As you can see in the above xml, we've added BSP_REV=9999 and __RPC_ARM32__ to the PreprocessorDefinitions tag.

After this modification you can build your SDK so it will contain the custom preprocessor definitions.

To automate this process you could, for example, create a batch script that will run after the make image phase (post-makeimage). This batch script could replace or modify the microsoft.cpp.sdk_name.ce800.props file. Getting a batch file to run after the make-image phase is easily done through the project properties:

Post-MakgeImage build step

Check out this blog post for ideas on how to create a useful batch file to add, replace or remove strings from arbitrary (text) files.

Adding (all) locales to your Windows Embedded Compact 7 OS

Introduction

This article explains how you can add all of the locales to your OS. You can find a brief description on the National/Regional Language Support (NLS) Catalog Items and Sysgen Variables page.

Although everything in this article relates to Windows Embedded Compact 7 (WEC7), the same is applicable to Windows Embedded Compact 2013 (WEC2013/CE8).

When I was porting a BSP and OSDesign from Windows CE 6.0 to Windows Embedded Compact 7, I ran into some issues regarding localization that I would like to share with you today.

Upgrading your OS from Windows CE 6.0 to Windows Embedded Compact 7

When you are in the process of upgrading or porting Windows CE 6.0 to Windows Embedded Compact 7 or Windows Embedded Compact 2013, you might face some hurdles to get it all working. Once you manage to overcome all hurdles and the operation system is running you're almost done. All you need to do now is adding some missing locales to the OSDesign. That can't be that hard, can it?

Well...

In the good old days of Windows CE 6.0 this was easy. You just had to select the locales you wanted to support in your OS in the properties page of your OSDesign. Simply right click on your OSDesign in the solution explorer, select properties and navigate to the locale tab. That tab page allows you to select the locales you want to support in your OS, then do a rebuild, and that's it.

Windows CE6 Locale Properties

Unfortunately, with Windows CE 7 this is all not so easy anymore. I have no idea why on earth this functionality was 'streamlined', but there are more decisions made around Windows CE that escape any sane reasoning I guess....

To get it all to work in Windows Embedded Compact 7 or 2013 you need to perform some extra steps, and that's exactly what this blogpost will explain. I'll also show you that it is not possible to add ALL, seemingly supported, locales to your OSDesign. To all those people living in Friesland (part of The Netherlands) that speak Frisian or those from Inuktitut (Canada) that use syllabics and some others; sorry, you're out of luck when it comes to Windows Embedded Compact!

In short you need to:

  • Add SYSGEN_NLS_XXX variables to the OSDesign
  • Manually add locales to nlscfg.inf to include all the locales needed for the .NET CF

To show what locales the OS running on your device supports, I created a little test program that enumerates over the available locales and will try to set the culture info and compare information associated with the locale. When you run the application you will see, unless you configured a different default locale, that only one locale "English (United States)" is listed. When you try to use a locale not supported by the OS, for example CultureInfo.CreateCulture("nl"), a PlatformNotSupportedException will be thrown by the managed application. This is reasonable because the locale was not supported in the first place.

The LocaleInfo test program looks like:

List<CultureInfo> missingLocale = new List<CultureInfo>();
List<CultureInfo> supportedLocale = new List<CultureInfo>();
foreach (CultureInfo ci in CultureInfoHelper.GetCultures())
{
    try
    {
        CultureInfo cultureInfo = CultureInfo.CreateSpecificCulture(ci.Name);
        CompareInfo compareInfo = cultureInfo.CompareInfo;
        supportedLocale.Add(ci);
    }
    catch (PlatformNotSupportedException)
    {
        missingLocale.Add(ci);
    }
}

// print out LCID
StringBuilder localeInfo = new StringBuilder();
foreach (CultureInfo locale in missingLocale)
{
    localeInfo.Append(locale.LCID.ToString("X4") + " ");
}

As you can see, the program enumerates the available locales and the Language Identifier (LCID) is stored (and printed), which we will use later. The test application (LocaleInfo.zip) is attached to this blogpost (in full source code). The test application will create a file (locale_info.txt) containing the locale information for the OS it is running on.

Here's what we need to do to add locale support to your OS:

Selecting the right SYSGEN_NLS_XXX variables

The easiest way to add components (sysgen variables) to your OS is to select the sysgen variables in the Catalog: open your OSDesign and locate the catalog window. Navigate to "International" and select the country of the locale you want to add. Let's take Africa as example. Expand "Afrikaans", just like in the screenshot below. Now if you want to add the locale "Afrikaans", make sure you select both variables, otherwise it will not work. So; add SYSGEN_NLS_AF and SYSGEN_NLS_AF_ZA. Note that in order to support one single language you need to select two sysgen variables!

Windows Embedded Compact 7 Locale Sysgen Variables

If you only want to support a few locales than going through the list and manually selecting the locales is not such a big problem. Just make sure that you select both variables per language/locale. Again an example; for the dutch language you'd need both SYSGEN_NLS_NL and SYSGEN_NLS_NL_NL.

In total, there are 197 locales available and if you want to add all those locales you will most likely end up with some form of RSI (Repetitive Strain Injury). To prevent this I created a little batch file that will set all locales that are supported in the catalog. I simply took the list of supported locales from <WINCEROOT>\public\CEBase\oak\misc\winceos.bat. If you're interested in that batch file please contact me and I'll send it to you.

Now that you have selected the locales you want and thus added sysgen variabls, you need to do a rebuild of the OS, so click Rebuild Solution (to understand what to build when read this blogpost).

If you select all available locales, rebuild the entire solution and run the test program again you will see, by examining locale_info.txt, that the application still detects unsupported locales. How is this possible? You've just added them all, didn't you?

Mysteries.. Apparently we need to add the missing locales manually to the OS:

Add locales to nlscfg.inf to support all locales needed for the .NET CF

Apparently adding the sysgen variables is not enough for some locales so we need to do a bit more, but first a little background information:

The .NET Compact framework depends on a 'list' of locales and this list is called the NLS Table. The NLS table describes the locale identifiers (LCIDs) available in your OS. For more information about the NLS Table and the nlscfg.inf, read this page on MSDN: Configuring the NLS Table. In short; the nlscfg.inf contains a list of LCIDs which should be in sync with the locales you enabled via the NLS sysgen variables. You can find the default nlscfg.inf in <WINCEROOT>\public\COMMON\oak\files.

When you run the test application LocaleInfo.exe, it will create a file containing locale information. A snippet from this generated locale_info.txt file:

Locale information current device
Missing locale LCIDs
------------------------------------------------
045E 3801 3C01 1401 0C01 0801 2C01 3401 3001 1001 1801 2001 4001 0401 2801 1C01
2401 047A 044D 082C 042C 046D 0423 0402 0845 0445 0451 047E 141A 0483 0405 0452
0465 0408 540A 0425 0429 0464 0462 083C 0484 0447 0468 040D 0439 101A 041A 042E
040E 042B 0470 0478 045D 0411 0437 043F 046F 0453 044B 0412 0457 0440 046E 0454
0427 0426 0481 042F 044C 0450 0850 047C 044E 043A 0461 046C 0482 0448 0446 0415
048C 0463 0486 046B 086B 0C6B 0417 0418 0419 0487 044F 0485 043B 045B 041B 0424
041C 1C1A 0C1A 301A 281A 181A 081A 2C1A 045A 0449 044A 0428 041E 0442 0432 041F
0444 085F 0480 0422 0420 0843 0443 042A 0488 0434 046A 0804 0C04 1404 1004 0404
0435
------------------------------------------------

The next step is to clone nlscfg.inf located in <WINCEROOT>\public\COMMON\oak\files to <WINCEROOT>\PLATFORM\<YOURBSP>\FILES and then add the list of missing LCIDs listed in locale_info.txt. The contents of the <WINCEROOT>\PLATFORM\<YOURBSP>\FILES folder are copied during the buildrel build phase and after that the makeimg phase will process and include all the locales listed in nlscfg.inf. If you click "Copy files to release directory" followed by a "Make Image", you will see that the makeimg build phase will exit with an error. The makeimg process will fail with error "The cenlscmp tool has exited with error code 1". If you examine the <WINCEROOT>\build.log, it will provide you with some additional information:

CeNlsCmp: Did not find locale 047a in locale file
error MSB3073: The command "cenlscmp <WINCEROOT>\<OSDesign>\..\RelDir\Shipbuild\postproc\nlssrc.txt
<WINCEROOT>\<OSDesign>\..\RelDir\Shipbuild\nlscfg.out
<WINCEROOT>\<OSDesign>\..\RelDir\Shipbuild\wince.nls
<WINCEROOT>\<OSDesign>\..\RelDir\Shipbuild\nlscpg.txt" exited with code 1.

The first line "CeNlsCmp: Did not find locale 047a in locale file" informs you that apparently information is missing for LCID 047a somewhere. I have unfortunately not been able to track down the missing information, so either there is still something missing or there is a bug in the tooling.

I have gone through the process of removing all LCIDs that cause the cenlscmp to break and provide you with a list of all 'supported locales' that can be enabled, just in case you want it 'all'. Attached to this blogpost you'll find the zipped nlscfg.inf file.

In the locale_info.txt (also attached to this blogpost), you'll find a summary of supported and non-supported locales:

Supported Locale

Total locales installed:197
Total locales supported:146
Total locales not supported:51

I hope that this information will save someone somewhere some time! ;-)

QT 5.6.0 Beta release is out!

Recently QT 5.6.0 Beta has been released. You can download the QT5.6.0 beta package from the QT download page.

So for anyone who followed the tutorial how to build QT for WEC2013 with the GuruCE iMX6 SDK, a new patch is available for download.

There are no changes in the build steps you only need to use the beta patch to run!

WEC2013 and Qt

Introduction

On a regular basis we receive requests whether it is possible to run Qt applications on platforms running our iMX6 BSP. To be able to run Qt applications you need a "Qt Application SDK" which is build against our GuruCE iMX6 SDK. This article will show you how to build the Qt Application SDK so you can run Qt applications on a device running the GuruCE iMX6 BSP.

This step described in this text apply to the QT5.6 alpha release which (sort of) includes WEC2013 support in combination with Visual Studio 2013. The WEC2013 support in this version of Qt is not completely working yet so you'll need to perform some additional steps to make it all work.

We'll show you exactly what you need to do, and while we're at it we'll add support for Visual Studio 2015 as well!

This article seems quite big but actually it isn't all that hard. For the impatient; you'll find a quick reference guide of all the steps to follow at the end of this article.

Qt 5.6 Alpha Release

The instructions in this article apply to the Qt 5.6 alpha release. As soon as we have tested the official 5.6 release, and perhaps other future releases, we'll update this article so it all works with the latest Qt release.

Qt is an open-source project and the git repositories allow you to get the latest and, sometimes not so, greatest developer branches. You can find instructions for getting the complete source code tree from the git repository here. Qt is a very active project, which is good in a way, but it also means there are a lot of commits every day. This can easily result in a broken build, especially since not every commit is checked against all the different platforms and build environments (which make sense in a way). Our suggested approach is to pick the latest stable release and make that work. Of course you can take the latest alpha, beta or even developer branch but in that case you may want to prepare yourself for some long days/nights chasing build errors! ;-)

[Update 21/12/2015]
QT 5.6.0 Beta is released. You can find the patch needed in order to build QT5.6.0 beta here. There are no other changes required in order to build QT for WEC2013 just as described in this article, except that you need to run the beta patch.

Qt Prerequisites

You'll need to download and install a couple tools and packages before you can start building the Qt application SDK:

  • Download and install Python. Make sure the Python binary folder is added to the system's %PATH% variable. This will be requested during installation.
  • Download and install Perl. Also the Perl binaries must be accessible by the build system, and so the binary folder also needs to be added to the %PATH% system variable.
  • Install Visual Studio 2013 or Visual Studio 2015.
  • Install Application builder for Embedded Compact 2013".
  • Download and install the latest GuruCE iMX6 SDK. You can pick any preferred platform. For Visual Studio 2015 you'll need the SDK of a release later than r550 (coming soon).
  • Download the Qt 5.6 alpha release from this location. Extract the source to any folder but make sure the extracted source code is on the same drive as the "working directory" (the directory where your generated binaries will end up).

The Qt 5.6 alpha source path used in this article is: H:\qt-everywhere-opensource-src-5.6.0-alpha
The Qt working directory used in this article is: H:\Qt\QT5.6a

Patch the Qt sources

The current 5.6 alpha release won't build for Windows Embedded Compact without some changes to the Qt source tree. We have packaged up all required patches and made them available for download here). All these patches are made by us and may not conform to the Qt coding standards, so please monitor the bug tracker for Qt to see if better solutions become available.

One of the patches also results in some mkspec files to be copied into the "Qt source tree\qtbase\mkspec" folder. These mkspec's contain the configuration information for the development environment and target platform. In our case we need mkspec files to indicate we use Visual Studio 2013 or Visual Studio 2015, the GuruCE iMX6 SDK and a Windows Development machine.

All said and done, lets build a Qt Application SDK now!

Building the Qt Application SDK

1. Install the patches

Download the Qt56alpha_patches.zip file and extract it in the root of the QT5.6a source path. You should end up with a "patch" folder after extraction (e.g. H:\qt-everywhere-opensource-src-5.6.0-alpha\patch). Navigate to this patch folder and run the patch.bat batch file.

If you want to undo the patches just open a Command Prompt in the patch folder and run:

H:\qt-everywhere-opensource-src-5.6.0-alpha\patch>patch -u

This will revert the changes made by our patches and restore the original Qt code tree.

2. Open Visual Studio 2013 or Visual Studio 2015 Developer Command Prompt

With Visual Studio 2013 there is no 'standard' start menu item for opening the Visual Studio 2013 "Developer command prompt", but shortcuts are available. Open a file explorer and navigate to the Shortcuts folder inside your Visual Studio installation folder, eg:

"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\Shortcuts"

Run the shortcut: "Developer Command Prompt for VS2013"

For Visual Studio 2015 there is a start menu item for the Developer Command prompt, just type "Command" in the desktop search pane and it should show up:

In both cases (VS2013 or VS2015) you can drag the developer command prompt shortcut to the desktop. This allows you to quickly open a developer command prompt whenever needed.

3. Set %PATH% variable to the source path

In the Visual Studio 2013/2015 developers command prompt, navigate to your Qt working directory (e.g. H:\Qt\QT5.6a). Remember that QT5.6a source path and the work directory must be on the same drive. Add the QT5.6a source path to the %PATH% environment variable by executing the following command (of course change the drive and folder name if needed):

set PATH=%PATH%;H:\qt-everywhere-opensource-src-5.6.0-alpha

You can also add this variable to the system environment variables. In that case you must not forget to change it in case you change the Qt working folder (for instance when you want to build a Qt Application SDK against a different Qt source tree). Setting the Qt source path in the command prompt is the most flexible way and allows you to build a Qt SDK against different Qt source trees.

4. Configure the Qt build environment

Execute the following command in the Visual Studio 2013 Developer command prompt (followed by enter):

H:\Qt\QT5.6a>configure -platform win32-msvc2013 -xplatform wince80GuruCE-armv7-msvc2013 -rtti -mp -ltcg -release -opensource -confirm-license -no-compile-examples -nomake tests -opengl es2 -openvg -skip qtandroidextras -skip qtactiveqt -skip qtsensors -prefix %CD%\SDK

Or for Visual Studio 2015 execute the following:

H:\Qt\QT5.6a>configure -platform win32-msvc2015 -xplatform wince80GuruCE-armv7-msvc2015 -rtti -mp -ltcg -release -opensource -confirm-license -no-compile-examples -nomake tests -opengl es2 -openvg -skip qtandroidextras -skip qtactiveqt -skip qtsensors -prefix %CD%\SDK

Now relax, take a deep breath and we'll go over the parameters while configure.bat runs (shouldn't take more than a minute or so).

The configure tool will generate the configuration and build tools needed to build Qt for the given platform and environment. Check the Qt documentation for detailed information regarding the configure tool.

    -plaform win32-msvc2013 -or- win32-msvc2015: Specifies Windows Desktop and Visual Studio version.
    -xplatform The target platform: the GuruCE iMX6 SDK.
    -rtti Compile run-time type information.
    -mp Enable multi-processors when building the SDK.
    -ltcg Use link-time code generation.
    -release Build a release configuration for the SDK.
    -opensource Compile and link the open source edition of Qt Check Qt licensing for more info.
    -confirm-license By specifying this parameter you automatically accept the selected Qt license (in our case this is the open-source license).
    -nomake Exclude a specific part from the build. In our case we skip building the tests. In case you want to save some disk space you can specify -nomake examples. This will leave out the Qt examples. In our case we want to use the examples later on so don't specify -nomake examples.
    -no-compile-examples Indicates not to compile the examples but include them as source in the SDK.
    -opengl The GuruCE iMX6 BSP fully supports hardware accelerated OpenGL ES2.0, so enable es20 in Qt.
    -openvg The GuruCE iMX6 BSP fully supports hardware accelerated OpenVG, so enable OpenVG in Qt.
    -skip This will exclude complete modules from the build. In our case we skip the qtandroidextras, qtactiveqt and qtsensors modules.
    -prefix This indicates the path of where to install the SDK locally. If you don't specify the -prefix parameter, everything will be installed in the working directory.

When configure.bat is finished you can review the used configuration settings by checking the "config.summary" file, which is located in the generated qtbase folder (in our case H:\Qt\QT5.6a\qtbase).

5. Build the Qt Application SDK; time for coffee!

To start the build process the only thing left to do is to enter the following command in the Visual Studio 2013/2015 Developer command prompt:

H:\Qt\QT5.6a>nmake

This process will take roughly an hour depending on your development machine configuration.

Install the SDK

When the build is done we can generate the Qt Application SDK installer. Type the following command followed by enter:

H:\Qt\QT5.6a>nmake install

This will generate the folder specified by the -prefix in the configure command line, in our case H:\Qt\QT5.6a\SDK.

The last thing we need to do is copy a qt.conf file into the SDK's bin folder. qt.conf is used to override hard-coded paths defined in the Qt libraries. The qt.conf file specifies where a Qt application can find libraries to link against. Every application needs a seperate qt.conf file if it's not present in the SDK\bin folder.

The contents of the qt.conf are quite simple:

[Paths]
Prefix=..

Download and extract the qt.conf.zip file and copy the qt.conf file to the generated SDK\bin folder (e.g. H:\Qt\QT5.6a\SDK\bin).

That's it! Now you can copy the SDK folder to the device. The size of the Qt Application SDK is approximately 420MB so this Qt Application SDK can be easily shared among developers.

Test Qt

When the Qt Application SDK is built, installed and it contains the qt.conf file in the SDK\bin directory, we can copy the Qt Application SDK to the device and run a Qt application:

  • Copy the SDK folder to the Windows Embedded Compact device.
    You can copy the SDK folder to the root of an SD card and insert the SD card into the device running CE. The Qt Application SDK folder contains an "examples" folder with sample applications you can try. To run the sample applications continue the steps below.
  • Telnet to the device.
  • Navigate to the Qt Application SDK bin folder: cd \SD\SDK\bin.
  • Start qml.exe with the following parameters, followed by ENTER:
\SD\SDK\bin>qml ..\examples\quick\animation\animation.qml

Telnet Screenshot

  • Choose any of the samples, for example "ColorAnimation":

    Running Qt Animation Application

    Is it really all that great?

    This article describes how to build the Qt Application SDK and how you can run a simple Qt application. We have not tested the Qt Application SDK more than what we described in this article. Testing all features of Qt is certainly possible, but our time is better spend adding low-level functionailty to our iMX6 BSP. We are certain there are more problems in the Qt source code than what we discovered so far; Windows Embedded Compact specific, but most probably also generic issues. Hopefully the final release of Qt 5.6 will solve a lot of the issues we have discovered. It's always good to participate and report issues to the Qt community, like we did as well. Current known issues we see in Qt 5.6 alpha are for instance:

  • qmlscene.exe sample application crashes. [update 18-12-2015] This is fixed now in the new patch
  • Visual Studio plugin is not accepting the Qt Application SDK build.
  • And probably some more...
  • Quick reference guide

    This is a summary of all the steps required to build the Qt Application SDK for the GuruCE iMX6 SDK:

    The Qt 5.6 alpha source tree location: H:\qt-everywhere-opensource-src-5.6.0-alpha
    The Qt working directory: H:\Qt\QT5.6a

    Nr. Description Command
    1 Make sure you meet the prerequisites and QT5.6a code tree is extracted (e.g. H:\qt-everywhere-opensource-src-5.6.0-alpha) -
    2 Download and extract the Qt56alpha_patches.zip file in the root of the QT5.6a source tree -
    3 Open a command prompt in the root of the Qt source code tree and run the patches: \H:\qt-everywhere-opensource-src-5.6.0-alpha\patch\patch.bat
    4 Exit command prompt exit
    5 Open VS2013 or VS2015 Developer command prompt and navigate to the work directory cd H:\Qt\QT5.6a
    6 Run the Qt configure tool for Visual Studio 2013 configure -platform win32-msvc2013 -xplatform wince80GuruCE-armv7-msvc2013 -rtti -mp -ltcg -release -opensource -confirm-license -no-compile-examples -nomake tests -opengl es2 -openvg -skip qtandroidextras -skip qtactiveqt -skip qtsensors -prefix %CD%\SDK
    - or -
    6 Run the Qt configure tool for Visual Studio 2015 configure -platform win32-msvc2015 -xplatform wince80GuruCE-armv7-msvc2015 -rtti -mp -ltcg -release -opensource -confirm-license -no-compile-examples -nomake tests -opengl es2 -openvg -skip qtandroidextras -skip qtactiveqt -skip qtsensors -prefix %CD%\SDK
    7 Build the SDK nmake
    8 Install the SDK nmake install
    9 Download and extract the qt.conf.zip file. Copy qt.conf to SDK\bin -
    10 Copy/FTP the Qt Application SDK to the device (~420MB) -
    11 Telnet to the device telnet 192.168.2.15
    12 Navigate to SDK location cd \sd\SDK\bin
    13 Run the animation sample qml ..\examples\quick\animation\animation.qml

    Update October 2020

    Thanks to Stan Morris we can now provide an update to all of the above. There are two issues with the above instructions and patches:

    • Missing configure argument which expose crash defect QTBUG-87767
    • mkspecs settings result in debug build stability issues

    Regarding the crash defect in Qt related to the build instructions in this post, the “configure” command line needs the “-qreal float” argument.

    The Qt 5.6 defect involves evaluating an uninitialized “double” in QML. Stan wrote up the defect in QTBUG-87767.

    This post’s linked Qt56alpha_patches.zip file contains mkspec files that have bad settings for debug builds which cause stability issues when debugging. Specifically, it is the /MTd flag that causes a problem. You can read a little about it on stack overflow.

    Bad directives:


    QMAKE_CFLAGS_DEBUG += -Zi -MTd
    QMAKE_CFLAGS_DEBUG -= -MDd

    Corrected directives:


    QMAKE_CFLAGS_DEBUG += -Zi

    WEC2013 GenSdk build error

    We recently ran into an issue with generating an SDK for WEC2013. The error message displayed wasn't very helpful:

    Error: GenSdk build error: Error HRESULT E_FAIL has been returned from a call to a COM component.

    In Visual Studio 2013 the error looks like this:

    With the help of Microsoft we managed to determine the SDK roller (GenSDK) couldn't find some required files and that was causing the failure. The "Build SDK tools" use some input/template files and folders to generate an SDK. It seems that it cannot build an SDK because some files/folders were deleted by the SDK build tools. This is confirmed to be a bug now... To fix this issue we need to regenerate the files and then rename a folder.

    Here are the steps to fix this problem for now:

    • Regenerate the files
    • When you build your solution the build process prepares the files needed for SDK generation by copying templates to the $(SG_OUTPUT_ROOT)\misc\sdk folder.

      In my test OSDesign the full path is: $(_WINCEROOT)\OSDesigns\TestCEPC\TestCEPC\Wince800\TestCEPC_x86_Retail\cesysgen\misc\sdk

      When you get the error Error: GenSdk build error: Error HRESULT E_FAIL has been returned from a call to a COM component this is because your $(SG_OUTPUT_ROOT)\misc\sdk folder has been corrupted. To fix this we need to regenerate the correct files. To do this you can either:

      1. Build your solution. This will generate the files in the correct place
      2. Copy the files manually
      3. Copy the contents from $(WINCEROOT)\public\common\sdk\msbuild\$(_TGTCPU)\sdk_name into the $(SG_OUTPUT_ROOT)\misc\sdk\msbuild\sdk_name folder.

      When everything is as it should be, the folder should look like this:

    • Rename the sdk_name folder
    • When the files are in the correct place you need to capitalize the sdk_name folder. In other words; rename it to SDK_NAME.

      So;

      Rename $(SG_OUTPUT_ROOT)\misc\sdk\msbuild\$(_TGTCPUFAMILY)\sdk_name to $(SG_OUTPUT_ROOT)\misc\sdk\msbuild\$(_TGTCPUFAMILY)\SDK_NAME.

      The reason for this is that the GenSDK managed code uses case-sensitive functions to search for the correct folder name. If the folder name is not all uppercase it will delete it!

      When you've renamed the folder you can now build your SDK again and all should be working fine!

    Microsoft is currently creating the update that will fix all of the above discovered problems.

    DISCLAIMER: This is a workaround, not a final solution. It will be fixed in one of the next updates. Please let me know if you find any issues with this workaround by leaving a comment below, thanks!

    Hosting webservices on WEC using gSOAP: Windows Phone 7.5 Mango Client Application

    In the previous 2 articles (this and this one) I showed you how to create .NET C# based client applications consuming a webservice running on a Windows Embedded Compact device. In this blog post I will show you how to do the same, but then with a client application running on Windows Phone 7.5!

    Convert RPC/encoded to RPC/literal

    When you try to use the existing wsdl file in a silverlight solution for WP7 you won't be able to use the operations as described. It seems that the silverlight version doesn't support RPC/encoded soap and ignores those operations. It does support RPC/Literal soap style so we need to change the style from RPC/encoded to RPC/literal. This is actually really easy: Open the wsdl file and modify the input and output operations from

    <soap:body use="encoded" ..../>

    to

    <soap:body use="literal" ..../>.

    In XMLSpy:

    You can see that the HelloWorld_Binding, the middle part, has changed. The input and output body styles have been modified from encoded to literal. That's basically all you need to change. This change in the wsdl file requires us to modify and rebuild the server and client code. Let's start with the server side.

    Rebuild HelloWorldWebService solution

    Open the existing "HelloWorldWebService.sln" solution we created in the previous blog post.

    1. Right-click the wsdl file and choose “compile”.
    2. Now recompile our (regenerated) header file: Right-click on the HelloWsdl.h file and choose "compile". This will re-generate a bunch of files as well.
    3. Finally; build the solution...

    Oops! Unresolved externals... The reason for these is that we changed the message style and therefore we need to construct (or actually gSOAP needs to construct) the soap message slightly different.

    In the HelloWorldWebservice solution open HelloWsdlMethods.cpp and modify the content to match the following:

    // int ns1__HelloWorldOperation(struct soap*, char *name, char* &answer)
    int ns1__HelloWorldOperation(struct soap*, char *name, struct ns1__HelloWorldOperationResponse &_param_1)
    {
            printf("Hello my method\r\n");
            char* myName = {"Erwin"};
            //answer = myName;
            _param_1.answer = myName;
            return SOAP_OK;
    }

    //int ns1__UploadResource(struct soap*, char *resourceName, xsd__base64Binary resource, unsigned int resourceId, unsigned int &resourceId_)
    int ns1__UploadResource(struct soap*, char *resourceName, xsd__base64Binary resource, unsigned int resourceId, struct ns1__UploadResourceResponse &_param_2)
    {
            printf("Upload resource called (name: %s, id: %d", resourceName, resourceId);
            int size = resource.__size;
            return SOAP_OK;
    }

    I commented the original code and added new code to emphasize the code differences.

    Windows Phone 7.5 (Mango) Client application

    Now lets create a Windows Phone 7.5 client application. Make sure you have the Windows Phone SDK 7.1 installed. You need this to build applications for WP7/7.5.

    Open Visual Studio 2010 Express for Windows Phone and create a new application (File | New Project). In the C# section "Silverlight for Windows Phone" choose "Windows Phone Application":

    Choose TestWebServiceWP7 as the solution name and leave the other options as is. Select "OK" to create the solution.
    I'll leave the sample as simple as possible just to illustrate how to consume a webservice running on a Windows Embedded Compact device. Drag 2 buttons on the content panel. Name the first button "Test" and the other "UploadImage". Something like:

    Double-click the test button so we can enter the code behind. First thing we need to do is add our (Web)Service reference. In the solution explorer right click on the "Service Reference" and choose "Add Service Reference". Select HelloWsdl, created in the beginning of this article. You should be seeing something like:

    As you can see I named the service "HelloWsdlService". Click "OK" to add the web service to our solution. Add the following code to complete the Test button implementation:

    namespace TestWebServiceWP7
    {
        public partial class MainPage : PhoneApplicationPage
        {
            private HelloWsdlService.HelloWorld_PortTypeClient service = null;

            // Constructor
            public MainPage()
            {
                InitializeComponent();

                // Create service
                service = new HelloWsdlService.HelloWorld_PortTypeClient();

                // Event handler for asyn operation hello world
                service.HelloWorldOperationCompleted += new EventHandler<HelloWsdlService.HelloWorldOperationCompletedEventArgs>(HelloWorldOperation_Completed);
            }

            private void Test_Click(object sender, RoutedEventArgs e)
            {
                try
                {
                    // Calling our webservice hello world
                    service.HelloWorldOperationAsync("Erwin");
                }
                catch (Exception ex)
                {
                    // Handle errors here
                }
            }

            void HelloWorldOperation_Completed(Object o, HelloWsdlService.HelloWorldOperationCompletedEventArgs args)
            {
                PageTitle.Text = args.Result.ToString();
            }
                   
        }
    }

    As you can see I create the service class and the event handler which handles the HelloWorldOperation asynchroneously in the constructor. The button test code (Test_Click) calls HelloWorldOperation. Deploy your solution to either the WP7 emulator or to your Windows Phone device. Run the webservice code on the Topaz (as describer in the other articles) and run the application on the WP7 emulator or device.

    To illustrate the complete solution check out this video showing me uploading and running the Windows Phone 7 application and the webservice on the Topaz. It is a recording of my desktop (I am using Cerdisp and Cerhost as an RDP solution to show the desktop of the Windows Embedded Compact device):

    [video:www.youtube.com/watch?v=vhPtS-SmkB4 width:640 height:385 ratio:16/9]

    If you have any questions please leave a comment below!

    Hosting webservices on Windows Embedded Compact using gSOAP: Uploading a file

    By popular demand here's a simple example how to upload a file using a gSOAP webservice. This is a follow up post; for this example you first need to read and implement the code from the first post in this series.

    With the base code of the first post it is now very simple to extend that solution's functionality because we've already completed the most difficult part; setting up your solution.

    In this follow up blog post I will create a method that allows you to upload a file to the Topaz target:

    int UploadResource(String resourceName, byte[] resource, uint resourceId, uint &statusCode);

    First we need to modify the existing wsdl file (our webservice design). I won't list the entire wsdl file here because it's getting a bit long. Instead I have downloaded a tool that allows me to graphically design my wsdl file; Altova XML spy. Altova XML spy is not a free tool but it is very nice to work with it. There may be some free tools that offer the same functionality as well (let me know if you find one). I opened the wsdl file we created in the previous blog post in this series and opened it in XML spy for modification. After modification it looks like this:

    Note the port type "UploadResource" I added (left block under HelloWorld_PortType). I added the input parameters resourceName (String), resourceId (unsinged integer) and a byte buffer for the actual data (base64Binary). I also added an output parameter statusCode. In the code I am not doing anything with it, but I just put it there to illustrate an output parameter. To complete the wsdl design we need to add the encodingStyle and namespace fields for the binding (the middle part of the picture). Just copy'n'paste it from the HelloWorldOperation. For complete reference I've attached the wsdl file to this blog post. It's a good example how to construct your webservice interface. It also shows a wsdl file can get complicated very quickly so a design tool like the one I used is recommended.

    Open the existing "HelloWorldWebService" solution we created in the previous blog post. You're now just a few simple steps away from finishing the server side code:

    1). Right click the wsdl file and choose “Compile”. Now our header (HelloWsdl.h2). Right click on the HelloWsdl.h file and choose "compile". Again this will re-generate a bunch of files as well.

    If you would now build your solution you will end up with some unresolved externals; our newly created method. You can find the declaration of this new method in the generated soapStub.h file:

    /******************************************************************************\
     *                                                                            *
     * Server-Side Operations                                                     *
     *                                                                            *
    \******************************************************************************/

    SOAP_FMAC5 int SOAP_FMAC6 ns1__HelloWorldOperation(struct soap*, char *name, char *&answer);

    SOAP_FMAC5 int SOAP_FMAC6 ns1__UploadResource(struct soap*, char *resourceName, xsd__base64Binary resource, unsigned int resourceId, unsigned int &statusCode);

    Open HelloWsdlMethods.cpp and add the following method implementation:

    int ns1__UploadResource(struct soap*, char *resourceName, xsd__base64Binary resource, unsigned int resourceId, unsigned int &statusCode)
    {
        printf("\r\nUpload resource called\r\nname: %s, id: %d ", resourceName, resourceId);
        return SOAP_OK;
    }

    Don't let the xsd__base64Binary type scare you. It's just a class with some members including:
    __ptr: A character pointer to the memory buffer (char* buf) containing the contents of the file.
    _size: Contains the number of bytes in the buffer above.

    4). Build your solution and run the server code on your Topaz!

    The C# Managed Client

    In the previous blog post in this series we've created a Testclient application written in C# to test our webservice. Open the "TestWebServiceClient" solution. Add an OpenFileDialog control and a button control to the Form. Call the button something like UploadFile:

    Double click on the newly added button which will create the button click event code behind. Now implement the event handler with the following content:

    // Opens a file dialog and uploads the file selected
    private void UploadFile_Click(object sender, EventArgs e)
    {
      if (openFileDialog1.ShowDialog() == DialogResult.OK)
      {
        byte[] data = GetBytesFromFile(openFileDialog1.FileName);
       
        if(data.Length > 0)
        {
           uint id = 22;
           try
           {
             server.UploadResource(openFileDialog1.FileName, data, ref id);
           }
           catch (Exception ex)
           {
             MessageBox.Show("Failed to upload resource" + ex.Message);
           }
        }
     }
    }

    // Reads a file and returns the content as a byte[]
    public static byte[] GetBytesFromFile(string fullFilePath)
    {
      // this method is limited to 2^32 byte files (4.2 GB)
      FileStream fs = File.OpenRead(fullFilePath);
      try
      {
        byte[] bytes = new byte[fs.Length];
        fs.Read(bytes, 0, Convert.ToInt32(fs.Length));
        fs.Close();
        return bytes;
      }
      finally
      {
        fs.Close();
      }
    }

    Of course this is just a sample and you should take of security and parameter checking. In this sample I have left this stuff out just to keep it as simple as possible.

    When you run the HelloWorldWebservice on the target device and run the C# Client sample on your pc, a file open dialog will appear when you click on the "Upload File" button. Select a file (I just selected desktop.ini) and it will upload the contents of that file to the webservice on the target.

    If you implemented everything correctly you should be seeing the following output on the target:

    That's it!

    To recap: To modify or extend your webservice, create/modify the wsdl. Compile the wsdl file, compile the header file. Implement the native webservice. When that's done, modify your client by refreshing the imported wsdl file and you can call your new webservice.

    Happy 2012 to all our readers!

    Hosting webservices on Windows Embedded Compact (Windows CE) using gSOAP

    In this blog post I'd like to discuss how to host webservices on a Windows Embedded Compact device. You can certainly consume webservices on a smart device using managed code, but hosting a webservice on the device is unfortunately not possible using managed code. So it's back to good old native code again!

    I will use gSOAP, a third party framework, that will do most of the nitty gritty work for us. Sounds easy right? Let's get started!

    The most important part is the WSDL (Web Services Description Language) file. The WSDL file is an XML document that describes the interface and thus which methods we want to expose and host on our device. I won't spend much time explaining the WSDL syntax as there are lots of online WSDL tutorials available that explain the syntax much better than I ever could.

    For now I just created a very simple WSDL file that describes one very simple webservice method: string HelloWorld(string name)

    The wsdl:

    <?xml version="1.0" encoding="utf-8"?>
    <definitions name="HelloService"
       targetNamespace="http://www.YourServer.com/wsdl/HelloService.wsdl"
       xmlns="http://schemas.xmlsoap.org/wsdl/"
       xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
       xmlns:tns="http://www.YourServer.com/wsdl/HelloService.wsdl"
       xmlns:xsd="http://www.w3.org/2001/XMLSchema">

      <message name="HelloWorldRequest">
        <part name="name" type="xsd:string"/>
      </message>
      <message name="HelloWorldResponse">
        <part name="answer" type="xsd:string"/>
      </message>

      <portType name="HelloWorld_PortType">
        <operation name="HelloWorldOperation">
          <input message="tns:HelloWorldRequest"/>
          <output message="tns:HelloWorldResponse"/>
        </operation>
      </portType>

      <binding name="HelloWorld_Binding" type="tns:HelloWorld_PortType">
        <soap:binding style="rpc"
           transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="HelloWorldOperation">
          <soap:operation soapAction="HelloWorldAction"/>
          <input>
            <soap:body
               encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
               namespace="urn:examples:helloservice"
               use="encoded"/>
          </input>
          <output>
            <soap:body
               encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
               namespace="urn:examples:helloservice"
               use="encoded"/>
          </output>
        </operation>
      </binding>

      <service name="HelloWorld_Service">
        <documentation>WSDL File for HelloService</documentation>
        <port binding="tns:HelloWorld_Binding" name="HelloWorld_Port">
          <soap:address
             location="http://Topaz:8080"/>
        </port>
      </service>
    </definitions>

    As you can see there are 6 main tags in the WSDL within the mandatory <definition></definition> tags:

    types
    Provides data type(s) used for the method parameters and return values.
    message
    Represents an abstract definition of the data being transmitted. A message consists of logical parts, each of which is associated with a definition within some type system.
    portType
    A set of abstract operations. Each operation refers to an input message and output messages.
    binding
    Specifies concrete protocol and data format specifications for the operations and messages defined by a particular portType.
    port
    Specifies an address for a binding, thus defining a single communication endpoint.
    service
    Used to aggregate a set of related ports and to set the network location of the actual service.

    Basically Type, Message and portType describe the webservice methods used and binding, port and service describe how to transfer the data over the socket.

    The location contains the IP address or hostname of the target that runs the server hosting the webservice. In this example I use a Topaz device (you would normally set this to the IP address or hostname assigned to your device).

    After this very brief WSDL description let's set up our Visual Studio 2008 project. This will be the biggest task.

    Before we can create the actual VS2008 project we need to perform a couple of steps (described in more detail below):

    1. Import the WSDL in an emtpy project
    2. Let gSOAP generate a header file via wsdl2header.exe
    3. Let gSOAP generate the corresponding implementation *.cpp file
    4. Implement our methods

    Because gSOAP generates a lot files I prefer to separate the generated files from the actual implementation files so that you only have to focus on your implementation and not what is being generated by gSOAP. So let’s begin and create an empty project in Visual Studio 2008 and add the WSDL file.

    Step 1: Import the WSDL in an empty project

    Open Visual Studio and create a new C++ smart device project (File menu | New | Project) and enter HelloWorldWebService as the name of the solution.

    Select Ok, and click next on the "Welcome to the Win32 Smart Device Project Wizard" dialog. In the "platforms" dialog select the SDK for your target device. As you can see I chose the Topaz device (http://guruce.com/topaz). Select next:

    In the "project setttings" dialog select "console application" and "empty project" and click finish.

    When the solution is created, go to the solution explorer (normally this would be visible by default but if not: go to View | Solution Explorer) and add a new filter "gsoap" in the "header" section (right click "Header Files", choose Add->New Filter) and do the same in the "source" section. We'll get gSOAP to generate its files there. Now create a new file with extension .wsdl in your solution's folder. Copy the contents of the sample WSDL above and add the WSDL file to your solution (right click on the solution and select “add existing item”). I've named the file HelloWsdl.wsdl. It should look something like this:

    Step 2: Let gSOAP generate the header file from our wsdl.

    First download gSOAP in order to get the tools needed. You can download gSOAP from this location:
    http://sourceforge.net/projects/gsoap2/files/
    Make sure you save the extracted files in an appropriate folder for you to remember because we need references to this folder within our visual studio project later on.

    Now go back to your solution and right click on the wsdl file and select properties. In the “Custom build step“ add the following command (note that I’ve put the gSOAP tools in the $(SolutionDir); make sure you get the path right and that the solution directory doesn't contain any spaces) in the “Command Line” section:
    $(SolutionDir)\gsoap-2.8\gsoap\bin\win32\wsdl2h.exe -s $(InputPath) -o $(ProjectDir)$(InputName).h
    This will run the wsdl2h.exe to generate our header file. The parameters specify the input and output file. In the “Outputs” field on the property page enter:
    $(InputName).h

    .

    You need to specify something in the “outputs” field in order to make the wsld ‘compilable’. Note that the path here contains a version number (gsoap-2.8). This can change of course so keep that in mind. Also notice that I did this "setting" for "All Configurations" and not just for the current one.

    Click “Apply” and “Ok”, and then right click the wsdl file again and choose “Compile”.
    Now our header (HelloWsdl.h) file is generated in the project directory and we need to add this file to the project. In the solution explorer right click on the “gsoap” folder in the header section and choose “add existing item”. Navigate to HelloWsdl.h and add it.

    Let’s do the same for generating the cpp file:

    Step 2: Let gSOAP generate the cpp source file.

    In the solution explorer right click on the HelloWsdl.h, which we just added in the previous step, and select “properties”. In the “Custom build step“ add the following command:
    $(SolutionDir)\gsoap-2.8\gsoap\bin\win32\soapcpp2.exe -S $(InputPath)

    In the “Outputs” field enter the following:
    $(InputName).cpp

    .

    Right click on the HelloWsdl.h file and choose "compile". This will generate a bunch of files as well, but we are not yet ready to build our solution... If you would try to build the solution at this time, like I did when I first started with gSOAP, you will run into a lot of compile errors so bear with me for a few more steps.

    Right click the gSOAP folder in the header section in your solution and add these header files:
    - soapH.h
    - soapStub.h

    Right click the gSOAP folder in the source section in your solution and add these cpp files:
    - soapC.cpp
    - soapServer.cpp

    The next step is to add stdsoap2.h and stdsoap2.cpp to your solution. You can find these 2 files in “gsoap-2.8\gsoap”. Add them together by right clicking the project in the solution explorer and select “Add existing item”. They will automatically appear under the correct “header” and “source” sections.

    We’ve added the stdsoap2.h to our solution but we also need to add the directory where stdsoap2.h resides to the project's "include directory list". Add “$(SolutionDir)\gsoap-2.8\gsoap” to the include directorie list by right clicking the project in the solution explorer and click "Properties". In the “Configuration Properties" | C/C++ | General” section you will find “Additional Include Directories”.

    Now that we’re done generating files we can actually start to code!

    Step 4: Implement our methods

    First we need to create the cpp source code containing the main definition and our methods. I will use 2 separate files for this. The first file will contain the server code and will listen to incoming requests/messages. The second file will actually implement our webservice's methods.

    Right click on the “Source Files” in the Solution Explorer and select “New Item”. Choose C/C++ file and name the file HelloWsdl.cpp. Do exactly the same for a file called HelloWsdlMethods.cpp.

    Your complete solution should now look like this:

    .

    Let’s start with an easy one; open the HelloWsdlMethods.cpp and copy and paste the following code snippet into that file:

    #include "soapH.h"

    int ns1__HelloWorldOperation(struct soap*,
                                 char*  name,       /// Request parameter
                                 char*  &answer     /// Response parameter
    )
    {
        printf("Hello my method\r\n");

        char* myName = {"Erwin"};
        answer = myName;
        return SOAP_OK;
    }

    Now if you have built the project prior to adding this piece of incredible intelligent code, you would have seen an unresolved external in the error list: this method. The above function is generated (or better; declared) by gSOAP and the code snippet above is the implementation. You can find the declaration of this method in the generated soapStub.h file:

    /************************************************************************\
    * Server-Side Operations                                                    
    \************************************************************************/

    SOAP_FMAC5 int SOAP_FMAC6 ns1__HelloWorld(struct soap*, char *name, char *&answer);

    This is where you will find your method's declarations when you have added your own in the WSDL.

    We’re almost there! The last thing we need to do is add our server code. Code that will wait for a request from any client. Below is the code needed. It may look complicated at first but don’t let it scare you. This code is taken from the gSOAP website (section 7.2.3 How to Create a Stand-Alone Server in the documentation section: link listed below) with some minor changes that I will describe below:

    /** Include the namespaces struct */
    #include "HelloWorld_USCOREBinding.nsmap"

    int _tmain(int argc, char* argv[])
    {
       struct soap soap;
       int m, s; // master and slave sockets
       soap_init(&soap);
       soap_set_namespaces(&soap, namespaces);      //** Set the namespaces **/
       m = soap_bind(&soap, "", 8080, 100);         //** leave the string empty and gSOAP will figure out what our "localhost" is **/
       if (m < 0)
          soap_print_fault(&soap, stderr);
       else
       {
          fprintf(stderr, "Socket connection successful: master socket = %d\n", m);
          for (int i = 1; ; i++)
          {
             s = soap_accept(&soap);
             if (s < 0)
              {
                soap_print_fault(&soap, stderr);
                break;
             }
          fprintf(stderr, "%d: accepted connection from IP=%d.%d.%d.%d socket=%d", i,
                (soap.ip >> 24)&0xFF, (soap.ip >> 16)&0xFF, (soap.ip >> 8)&0xFF, soap.ip&0xFF, s);
          if (soap_serve(&soap) != SOAP_OK) // process RPC request
          soap_print_fault(&soap, stderr); // print error
          fprintf(stderr, "request served\n");
          soap_destroy(&soap);      // clean up class instances
          soap_end(&soap)// clean up everything and close socket
          }
       }
       soap_done(&soap); // close master socket and detach context

       return 0;
    }

    Copy and paste the code above into HelloWsld.cpp.

    I've listed my changes with some comments (/** */). What is added is that we include a namespaces struct to explicitly set the correct namespaces. gSOAP (soapcpp2.exe) will not only generate code files but also a *.nsmap file which generates a static struct containing the correct namespace to use. The soap_set_namespaces() method will set this struct to use it. That’s it!

    All of this may seem like a lot of work but when you are finished setting up the project every change you make in your interface (wsdl) will automatically drill down into your implementation. After setting up the project you most likely will only need to work on the wsdl file and HelloWsdlMethods.cpp to add your own methods.

    Now that we've created the server on Windows CE it is time to create a client that will consume our service:

    The C# Managed Client

    Create a C# .NET (desktop) application with just one Form (I'm not going through each single step of how to create a C# .NET application as this is outside of the scope of this article and assumed known). Add just one button to the form and give it a name. After that we need add our webservice reference to the solution (our wsdl file). In the solution explorer right click on "References" and select "Add Service Reference". In the address field enter the path and the name of the wsdl file on your local machine. In my case this is: C:\Data\Development\HelloWorldWebService\HelloWorldWebService\HelloWsdl.wsdl. I named the reference HelloWsdlMethods and this name will appear in the solution explorer. Click "Go":

    .

    On the main form Double click on the button and paste the following code into the button handler:

    private void btnTest_Click(object sender, EventArgs e)
    {
        try
        {
            HelloWsdlMethods.HelloWorld_PortTypeClient server = new HelloWsdlMethods.HelloWorld_PortTypeClient();
            string name = server.HelloWorldOperation("Dummy");
            MessageBox.Show(name);
        }
        catch (Exception error)
        {
            MessageBox.Show("Request failed: " + error.Message);
        }
    }

    The code behind the button will access our server and call the webservices method. As you can see the naming is probably not the best but for this example it will do.
    Run the server program (HelloWorldWebService.exe) on the target and run the client on the desktop. If you've done everything correct you should see the following after pressing the "Test" button:
    The C# Client running on the desktop:

    Application console on the Windows Embedded Compact device:

    Things to lookout for

    As you have seen there is a bit of work involved to get gSOAP to integrate nicely with Visual Studio 2008. The process is error prone and gSOAP's logging doesn't help much in most cases. In my experience there are basically three areas where things are most likely to go wrong:

    • Location
    • This tag in the wsdl file specifies the network address on which the webservices are running. Make sure it is set right.

    • Namespaces
    • Make sure that the namespaces used in the wsdl file match the ones in the source code (use the nsmap file) and don't forget to call soap_set_namespaces().

    • Hostname
    • Make sure that you leave the hostname empty: soap_bind(&soap, "", 8080, 100). Specifying the hostname does not work on CE. Also the tools wsdl2h.exe and soapcpp2.exe have a lot of options that I did not discuss in this article. However, getting to know the different gSOAP options is definitely worth the time. The gSOAP website contains a wealth of information and the documentation is comprehensive.

    Of course this article only shows a very simple example of how to use Webservices on Windows CE with gSOAP but it should be enough to get you going on much more complex webservices hosted on smart devices.

    Please let us know if you'd like to see a more complex example (for instance transferring a file through webservices).

    Good luck!

    Programmatically instantiate the password login dialog

    In Windows CE there is built in functionality that allows a user to lock and login the system, just like you are used to on "big" desktop systems. To activate this functionality you simply set the system password in Windows CE through the control panel applet. You can also do this programmatically using the SetPassword() and SetPasswordStatus() API's.

    When the password is set at boot the system will show the standard Windows CE login screen:

    Recently I got a question whether it is possible to programmatically bring up the password dialog. After some analysis the answer was yes. I found out that the Power Manager instantiates the password dialog when the system returns from showing a screensaver. The password dialog is instantiated by calling ShowStartupWindow(). Here's the code used by the Power Manager (from "<WINCEROOT>\PUBLIC\COMMON\OAK\DRIVERS\PM"):

    hmCoreDll = (HMODULE) LoadLibrary(_T("coredll.dll"));
    gpfnShowStartupWindow = (PFN_ShowStartupWindow) GetProcAddress(hmCoreDll, _T("ShowStartupWindow"));

    The example below shows you how to use the exact same technique inside your applications to instantiate the password dialog:

    #include <windows.h>

    // We need to declare these functions here because they are defined
    // in pwinbase.h (contrary to what MSDN help for "SetPassword" tells
    // us!) and pwinbase.h is not included as an SDK header file.
    // Therefore to use these functions without needing access to all
    // Platform Builder include files, we have to define them ourselves.
    extern "C"
    {
        BOOL SetPassword (LPWSTR lpszOldpassword, LPWSTR lspzNewPassword);
        BOOL SetPasswordStatus(DWORD dwStatus, LPWSTR lpszPassword);
    }

    // Forward declarations
    bool InitPassword(LPWSTR pszOldPassword, LPWSTR pszNewPassword);

    // Defines
    #define PASSWORD_STATUS_ACTIVE              1
    #define PASSWORD_STATUS_SCREENSAVERPROTECT  2

    // Typedef'ed functions
    typedef BOOL (WINAPI *PFN_ShowStartupWindow)(void);

    int _tmain(int argc, _TCHAR* argv[])
    {
        BOOL bRet = false;
        HMODULE hCoreDll = NULL;
        PFN_ShowStartupWindow pfnShowStartupWindow = NULL;

        // Create and activate password
        // (you can supply the current ("old") password on the command line)
        LPWSTR pszOldPassword = (argc > 1) ? argv[1] : NULL;
        if (InitPassword(pszOldPassword, L"password"))
        {
            hCoreDll = (HMODULE) LoadLibrary(L"coredll.dll");
            if (hCoreDll)
            {
                pfnShowStartupWindow = (PFN_ShowStartupWindow)GetProcAddress(hCoreDll, L"ShowStartupWindow");
                if (pfnShowStartupWindow)
                    bRet = pfnShowStartupWindow();
                else
                    RETAILMSG(1, (L"Failed to get address of \"ShowStartupWindow\" in coredll.dll, error %d\r\n", GetLastError()));
                FreeLibrary(hCoreDll);
            }
            else
                RETAILMSG(1, (L"Failed to load coredll.dll, error %d\r\n", GetLastError()));
        }
        return (int)bRet;
    }

    bool InitPassword(LPWSTR pszOldPassword, LPWSTR pszNewPassword)
    {
        bool bRet = false;
        // Please note that the password must be lower case when you set it!
        // The password dialog box (code in startui.cpp) always converts the
        // user entered password to lower case before the compare!
        if (!SetPassword(pszOldPassword, pszNewPassword))
            RETAILMSG(1, (L"Failed to set the password, error %d\r\n", GetLastError()));
        //Set the password status
        else if (!SetPasswordStatus(PASSWORD_STATUS_ACTIVE | PASSWORD_STATUS_SCREENSAVERPROTECT, pszNewPassword))
            RETAILMSG(1, (L"Failed to set the password status, error %d\r\n", GetLastError()));
        else
            bRet = true;
        return bRet;
    }

    Of course you can always create your own password dialog (because let's be honest; it's not the most beautiful dialog you've ever seen, right?!), but if you don't mind the ugly box the technique above just uses the existing functionality of CE and will save you a bit of time developing a password dialog yourself.

    From the comments in the code above you can see I found a rather interesting little fact: Windows CE passwords are CASE INSENSITIVE! That's old-school, isn't it?!

    The code that handles the GUI portion of the password handling code is in startui.cpp (here "<WINCEROOT>\PUBLIC\COMMON\OAK\DRIVERS\STARTUI"):

    //      Get text from password window.
    SendMessage(hwndPass, WM_GETTEXT, PASSWORD_LENGTH + 1, (LPARAM)szText);
    _wcslwr(szText);
    isAuthValid = (CheckPassword(szText) != FALSE);

    The _wcslwr function converts the password string to lowercase before checking it, but SetPassword allows you to set a password using mixed case! Keep that in mind next time you locked yourself out of your Windows CE device... ;o)

    SD MMC and Windows CE

    Lately there have been a lot of questions about SD and MMC in the newsgroups, especially about what is supported by the Microsoft SD bus driver. This blog post hopefully helps clear up some things about SD/MMC support in Windows CE.

    First let’s look at an overview of the MMC and SD specifications and who supports what:

    Specification overview
    CE Version SD Spec MMC Spec
    Win CE 5.0 RTM 1.1 3.x
    Win CE 5.0 QFE (April 2007 onward) 2.0 4.3
    Win Mobile 6.0 RTM 1.1 3.x
    Win Mobile 6.x (AKU 0.2 onward) 2.0 4.3
    Win CE 6.0 RTM 1.1 3.x
    Win CE 6.0 R2 2.0 4.3

    From the table above it looks like the SD bus driver in CE 6.0 R2 supports the MMC 4.3 specification, but actually it doesn’t completely (as we’ll see a bit later in this blog post).

    The Microsoft SD bus driver (sdbus2.dll) is fully supporting the SD 2.0 specification. Because the MMC 4.3 specification is quite similar to SD 2.0, CE “somewhat” supports MMC 4.3.

    So what does this mean? Well, for example, the number of data lines (bus width) of MMC supported by the Microsoft SD bus driver differs from the MMC specification:

    8/4 or 1-bit mode

    The MMC specification tells us it supports 8-bit wide bus mode (8-data lines). However, the SD 2.0 specification does not support 8 bit bus mode. Since CE officially supports SD but not MMC, CE does not support the 8-bit mode for any MMC/SD card. Unfortunately it appears the 4-bit mode is also not supported for MMC cards by the Microsoft SD bus driver even though the SD specification does support this mode. This leaves 1-bit mode as the only supported mode for MMC.

    Here’s an overview of the supported modes by the SD bus driver (sdbus2.dll):

    Bus mode SD MMC
    1 Y Y
    4 Y N
    8 N N

    So does this mean that MMC cannot support 4 or 8 bit at all? Well no, not really... You can always CLONE the SDBUS driver and modify it according to your needs or you can develop your own driver without using the SD bus driver at all.

    High Capacity

    According to the SD 2.0 specification "High Capacity" means cards with sizes larger then 2GB. SDHC cards are supported by the SD bus driver version 2.0. However, because of a bug in the SD bus driver high capacity MMC cards don’t work. You need to fix one line of code to support HC MMC cards. Besides this bug there is also a difference in the protocol for MMC and SD 2.0 regarding High Capacity, more about that later.

    First let’s fix the obvious bug so that your High Capacity MMC card will be recognized and mounted properly:

    The first step is to clone the SD Bus driver located at <WINCEROOT>\PUBLIC\COMMON\OAK\DRIVER\SDCARD\SDBUS

    1. Open a build release window
    2. Type "cd %_targetplatroot%"
    3. Type "cd src"
    4. Type "cd drivers"
    5. Type "md sdbus2"
    6. Type "cd sdbus2"
    7. Type "sysgen_capture -p common sdbus"
    8. Now copy the files from <WINCEROOT>\PUBLIC\COMMON\OAK\DRIVER\SDCARD\SDBUS into <WINCEROOT>\PLATFORM\<YourBSP>\SRC\DRIVERS\SDBUS2
    9. And merge sources.sdbus and sources so you end up with this sources file:
    10. SYNCHRONIZE_BLOCK=1

      TARGETNAME=sdbus
      TARGETDEFNAME=SDBus2
      DEFFILE=$(TARGETDEFNAME).def

      TARGETTYPE=DYNLINK
      RELEASETYPE=PLATFORM

      DLLENTRY=_DllEntryCRTStartup

      SOURCES = sdbusreq.cpp \
                sddevice.cpp \
                sdbus.cpp \
                sdslot.cpp \
                sdclient.cpp \
                sddevinf.cpp \
                sdiofeat.cpp \
                sdworki.cpp \
                sddebug.cpp \

      TARGETLIBS=                                           \
        $(_SYSGENOAKROOT)\lib\$(_CPUINDPATH)\defbuslib.lib  \
        $(_SYSGENSDKROOT)\lib\$(_CPUINDPATH)\coredll.lib    \
        $(_SYSGENOAKROOT)\lib\$(_CPUINDPATH)\ceddk.lib    

      Note that I added SYNCHRONIZE_BLOCK=1 to make sure any other component that will link to the sdbus library will be able to find it. SYNCHRONIZE_BLOCK=1 makes sure this folder is built before any other folder in the DRIVERS folder is built.

    11. Try to build the SDBUS2 driver; it should build without errors. If it doesn’t, make sure you selected the SD Bus Driver in the catalog and performed a sysgen on your OSDesign.
    12. And add the SDBUS2 folder to the dirs file in the DRIVERS folder.
    13. Don't forget to change any BSP component that links to $(_COMMONOAKROOT)\lib\$(_CPUINDPATH)\sdbus.lib to use the cloned SDBUS library, located at $(_TARGETPLATROOT)\lib\$(_CPUINDPATH)\sdbus.lib

    Now change StringCchCopy to StringCchCat in line 1394 of sddevice.cpp (in your cloned folder of course!). This will append the "\\High_Capacity" string to the Client driver registry entry instead of replacing it (which leads to not finding the correct registry path for the profile because "HKLM\\High_Capacity" is not a valid registry path). Even with this fix it won’t find that path because Microsoft did not provide registry settings for High Capacity MMC. Let’s fix that by adding the following to platform.reg:

    ; SDHC Memory Storage class driver
    [HKEY_LOCAL_MACHINE\Drivers\SDCARD\ClientDrivers\Class\MMC_Class\High_Capacity]
       "Dll"="SDMemory.dll"
       "Prefix"="DSK"
       "BlockTransferSize"=dword:40  ; send no more than 64 blocks of data per bus transfer
       ;"SingleBlockWrites"=dword:1  ; alternatively force the driver to use single block access
       ;"IdleTimeout"=dword:7D0      ; 2000 milliseconds
       ;"IdlePowerState"=dword:2     ; 0 == D0, 1 == D1, etc.
       ;"DisablePowerManagement"=""  ; if value present, then disable (remove value to enable)

       "Profile"="MMC"
       "IClass"=multi_sz:"{A4E7EDDA-E575-4252-9D6B-4195D48BB865}",
                         "{8DD679CE-8AB4-43c8-A14A-EA4963FAA715}"

    Now you can Build and Sysgen your BSP by right clicking on your BSP node in the Solution Explorer and choosing Build and Sysgen. NEVER EVER click Build and Sysgen from the Build menu, please read the blog post http://www.guruce.com/blogpost/whattobuildwhen to understand why you should delete that option from the build menu. After the Build and Sysgen of the BSP perform a Copy files to release directory followed by a Make image and download your kernel to your device. When you now insert a high capacity MMC card it should be recognized and mounted correctly, well, almost! The size is most probably still reported wrong. This is because of a difference in the protocol. CMD 8 is handled differently in the SD 2.0 and MMC 4.3 specification:

    COMMAND 8 (CMD8)
    Command Type Response Abbreviation Command Description
    CMD8 MMC R1 SEND_EXT_CSD The card sends its EXT_CSD register as a Block of data.
    CMD8 SD R7 SEND_IF_COND Sends SD Memory Card interface condition

    In the SD protocol CMD8 is used to identify high capacity cards and it is send at the beginning of the initialization process. When CMD8 is getting a response it means that it is identified as a High Capacity card. If sending a CMD8 results in a response timeout it is not a High Capacity card.

    For MMC the CMD8 means retrieving the density of the card from the EXT_CSD register instead of the CDS register. CMD8 must be send after a CMD7 command which will place the card into "Tran" state. Since the SD bus driver doesn't do that the CMD8 will time out.

    Like I said the SD bus driver fully supports the SD 2.0 specification and it reads the card density from the CSD register. This works for SD 2.0, but not for MMC. To fully support High Capacity MMC cards you should modify the SD bus driver so it can handle CMD8 instead of CMD9 to retrieve the card density for MMC high capacity cards.

    SDBUS or SDBUS2

    Now how do you select the correct SD bus driver? First remember that you have to install the correct version of Windows CE and install all required QFEs (see specification overview above). For Windows Embedded CE 6.0 there are two catalog components for SDBUS; "SD Bus Legacy" and "SD Bus driver". The legacy one is the SD bus driver that supports SD specification 1.1 and the "SD Bus Driver" item supports the 2.0 specification.

    For all other versions of CE there is only one catalog component which automatically selects the SD bus 1.1 specification. To support the 2.0 specification in CE versions prior to CE 6.0 you have to set the IMGSDBUS2 environment variable to 1.

    Conclusion

    Windows CE fully supports the SD 2.0 specification, but as we saw this doesn’t automatically mean it also fully supports High Capacity MMC cards. Fortunately with a couple modifications here and there you can now fully support High Capacity MMC cards as well!

    Command Line Build

    This post will show you how to create a batch file that will build your Windows CE OS without using the Visual Studio/Platform Builder IDE.

    Often the question is asked how to setup an environment which automatically extracts all information from version control and then builds your code. There are a lot of tools that can help you do that: an open source alternative can be found at http://cruisecontrol.sourceforge.net.

    To build your kernel without having to use the IDE create a batch file with the following content:

    @echo off
    SET _WINCEROOT=C:\WINCE500
    SET _OSDESIGNDIR=%_WINCEROOT%\PBWorkspaces\YOUR_WORKSPACE_FOLDER
    SET _OSDESIGN=%_OSDESIGNDIR%\YOUR_WORKSPACE_FILE.pbxml
    SET _OSDESIGNCONFIG=YOUR_OSDESIGN_CONFIG_NAME

    "%ProgramFiles%\Windows CE Platform Builder\5.00\CEPB\BIN\pbxmlutils" /getbuildenv /workspace "%_OSDESIGN%" /config "%_OSDESIGNCONFIG%" > SetEnv.bat

    cd "%_OSDESIGNDIR%"
    call SetEnv.bat
    del SetEnv.bat
    cd "%_OSDESIGNDIR%"
    blddemo clean -q

    Note that for CE 6.0 pbxmlutils is located in "%ProgramFiles%\Microsoft Platform Builder\6.00\cepb\IdeVS\" and you use SET _WINCEROOT=C:\WINCE600

    You need to replace the first 3 SET variables to match your specific project:

    YOUR_WORKSPACE_FOLDER: Workspace folder which is located under the PBWorkspaces (CE 5.0) or OSDesigns (CE 6.0) folder. This is the folder that contains YOUR_WORKSPACE_FILE.

    YOUR_WORKSPACE_FILE: The name of your OS Design workspace; the file with extension .pbxml. This file is located in YOUR_WORKSPACE_FOLDER.

    YOUR_OSDESIGN_CONFIG_NAME: This is the configuration name you select in the IDE of Platform Builder, eg Emulator: x86_Release. You can also open your .pbxml file with notepad to find out what the configuration name is (search for Configuration Name).

    Happy building!

    Windows CE Task Manager

    I found a great tool to monitor the CPU load on your Windows CE device, which includes the source code!

    http://urana.info/mobile/wince/itaskmgr/index.html

    Subscribe to RSS - Erwin's blog