Erwin's blog

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):

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
delete 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

Syndicate content