Welcome to MSDN Blogs Sign in | Join | Help

WCF for NETCF Beta2 Update

Lately, I've been working on getting my WCF for NETCF hello world BLOG posts (Intro to Messaging and the Intro to Store and Forward Messaging)  prepared for inclusion into Orcas.  I have both examples running and debugging with Orcas Beta2 and NETCF v3.5 Beta2.  The code has not changed much from the original Blog posts (I did fix a coding error).  I've back updated the two BLOG posts with the current code taken from the samples.

Store and Forward Channel Beta1... Take Two!

The .NET Compact Framework v3.5 Beta1 was shipped in Orcas Beta1 allowing developers to try out NETCF's new features, except for the Store and Forward Transport which has a strong name signing issue.  This issue stops compilation of Smart Device projects referencing Microsoft.ServiceModel.Channels.Mail.dll and or Microsoft.ServiceModel.Channels.Mail.WindowsMobile.dll.  We have fixed this issue and are making the fix available through the end user download.  If you’re trying the Store and Forward Channel out, first uninstall .NET Compact Framework v3.5 Pre-Release then install it from http://www.microsoft.com/downloads/details.aspx?FamilyID=1343d537-a62f-4a6e-9727-7791bf4cc2bd&DisplayLang=en

Introduction to the Windows Communication Foundation for the .NET Compact Framework, Store and Forward Messaging

Lets continue the discussion of the .NET Compact Framework new WCF features by discussing the new EMAIL transport for store and forward messaging.  This BLOG builds off my previous BLOG on WCF deliverables included in NETCF 3.5.  I suggest you read my previous BLOG  before reading this BLOG, I also suggest you read Roman's BLOG as background.  Let’s take a closer look at this new feature by describing some of the key features discuss a few best practices and look at a HelloWorld example.

The EMAIL transport permits device addressability so that messages can be sent to a device and messages from the device can be stored up and forwarded on the next synchronization.  This allows developers to create applications for devices in the field to communicate their data to a local store.  The data is then transferred using ActiveSync's Always Up to Date passing the data through Exchange 2007 directly to the data center.  For more information on AUDT see: http://www.microsoft.com/technet/prodtechnol/exchange/2003/autd.mspx.  This feature also allows data to be pushed from the data center down to one or more devices; previously this was not an easy task! 

This communication is one way, the device can send a message to the data center or the data center can send a message to the device.  Each listener is limited to a specific channel name and will block while waiting for a message to be delivered.  For this reason, we suggest you spin up a separate thread for each channel listener allowing the data from the thread to be processed as needed and not block the entire application.

Using the email local store to store messages, allows the device to reboot or the application to be restarted while not interrupting the flow of messages.  To address this type of message delivery, your program will need to use a stateless programming model.

The sample below is an iteration of the http hello world example from my previous post.  It demonstrates the new EMAIL transport features in an easy to read format  Use this sample to help you start your investigation into Store and Forward Messaging, do not copy and paste it into production code.  This code will only run with the new EMAIL transport which ships with the .NET Compact Framework v3.5 beta1 which is being released with Orcas Beta1.

Configuring Your PC to run the Store and Forward Messaging Hello World.

First you will need the Beta2 or later version or later of the next Visual Studio code named Orcas.  Check http://msdn2.microsoft.com/en-us/vstudio/default.aspx for the latest updates.

Next you will need the Windows Mobile 6.0 SDK as it includes the ActiveSync Always up to Date features used by the EMAIL transport on device to keep the inbox up to date, see: http://www.microsoft.com/windowsmobile/default.mspx

The EMAIL transport on the data center side will only run on Exchange 2007.  If you have an Exchange 2007 server running on your network you can skip this section.  The Exchange team has made a trial in VPC form available, its a quick place to start. See http://www.microsoft.com/exchange/default.mspx for more details.   Before you move on, I suggest you validate your connection to the new Exchange server by running Outlook Web Access (OWA) from your PC.

Next you need to provide your emulator with network capabilities.  (ActiveSync's Always up to Date Feature does not run when ActiveSycn is providing the network connection.) For more on enabling networking with the emulator see: http://blogs.msdn.com/anandba/archive/2007/03/13/networking-support-on-device-emulator-in-vista.aspx

Once you have the Emulator up and running, next you need to configure ActiveSync to communicate with your Exchange Server.  See the Connecting to an Exchange Server by Using a Phone or a Wireless Network at: http://www.microsoft.com/technet/solutionaccelerators/mobile/deploy/msfp_8.mspx

I've updated the code below to reflect the changes found in Orcas Beta2.

 Store and Forward Messaging Hello World, the Device:

To begin with, you will need to create a new Smart Device project in Orcas.  You can use the Windows Mobile 5.0 or 6.0 SDK.  I used WM 6.0 Professional.

To start you need to add a reference to the device EMAIl transport Microsoft.ServiceModel.Mail.dll and Microsoft.ServiceModel.Mail.WindowsMobile.dll

Next lets add the needed using statements.

using System.Runtime.Serialization; using Microsoft.ServiceModel.Channels.Mail; using Microsoft.ServiceModel.Channels.Mail.WindowsMobile; using System.ServiceModel; using System.ServiceModel.Channels; using System.Xml.Serialization; using System.Xml;

Lets now add the TransmittedObject and XmlSerializerWrapper class from the previous post.

[System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://Microsoft.ServiceModel.Samples")] public class TransmittedObject { [System.Xml.Serialization.XmlElementAttribute(Order = 0)] public string str; [System.Xml.Serialization.XmlElementAttribute(Order = 1)] public int i; }
public sealed class XmlSerializerWrapper : XmlObjectSerializer { XmlSerializer serializer; string defaultNS; Type objectType; public XmlSerializerWrapper(Type type) : this(type, null, null){ } public XmlSerializerWrapper(Type type, string name, string ns) { this.objectType = type; if (!String.IsNullOrEmpty(ns)) { this.defaultNS = ns; this.serializer = new XmlSerializer(type, ns); } else { this.defaultNS = ""; this.serializer = new XmlSerializer(type); } } public override bool IsStartObject(XmlDictionaryReader reader) { throw new NotImplementedException(); } public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName) { throw new NotImplementedException(); } public override void WriteEndObject(XmlDictionaryWriter writer) { throw new NotImplementedException(); } public override void WriteObjectContent(XmlDictionaryWriter writer, object graph) { throw new NotImplementedException(); } public override void WriteStartObject(XmlDictionaryWriter writer, object graph) { throw new NotImplementedException(); } public override void WriteObject(XmlDictionaryWriter writer, object graph) { this.serializer.Serialize(writer, graph); } public override object ReadObject(XmlDictionaryReader reader) { string readersNS; readersNS = (String.IsNullOrEmpty(reader.NamespaceURI)) ? "" : reader.NamespaceURI; if (String.Compare(this.defaultNS, readersNS) != 0) { this.serializer = new XmlSerializer(this.objectType, readersNS); this.defaultNS = readersNS; } return (this.serializer.Deserialize(reader)); } }

Now we have enough information to build the message.

            TransmittedObject to = new TransmittedObject();
            to.str = "Hello";
            to.i = 5;

            XmlSerializerWrapper wrapper = 
                new XmlSerializerWrapper(typeof(TransmittedObject));

            Message m = Message.CreateMessage
                (MessageVersion.Default, "urn:test", to, wrapper);

Next let’s setup a few variables used in the rest of the program, the device and server e-mail addresses and the channel name.

string channelName = "StoreandFowardMessageHelloWorld"; string serverAddress = "ServerMailAddress@ExchangServer.com"; string clientAddress = "DeviceMailAddress@ExchangServer.com";

Let’s build the output channel and send the message to the server.

            WindowsMobileMailBinding binding = new WindowsMobileMailBinding();
            BindingParameterCollection parameters = new BindingParameterCollection();

            //Create and open the Channel Factory
            IChannelFactory<IOutputChannel> channelFactory =
                                            binding.BuildChannelFactory<IOutputChannel>(parameters);
            channelFactory.Open();
            IOutputChannel outChannel = channelFactory.CreateChannel(
                                new EndpointAddress(MailUriHelper.Create(channelName, serverAddress)));
            outChannel.Open();

 Time to send the message.

      outChannel.Send(m);

Now the message has been sent, we need to listen for the response.  The listener will block so I would suggest in your production code to run any listener on a separate thread.  For this example just place it below the sender.

            IChannelListener<IInputChannel> listner = binding.BuildChannelListener<IInputChannel>
                                      (MailUriHelper.CreateUri(channelName, clientAddress), parameters);

            listner.Open();

            IInputChannel inputChannel = listner.AcceptChannel();
            inputChannel.Open();

            Message reply = inputChannel.Receive();

Now that we have the responce, lets Deserialize it and present the results to the user.

            TransmittedObject to1 = 
                reply.GetBody<TransmittedObject>
                (new XmlSerializerWrapper(typeof(TransmittedObject)));

            MessageBox.Show(to1.str + " " + to1.i.ToString());

Finally lets clean up the EMAIL channel objects.

            outChannel.Close();
            channelFactory.Close();

            listner.Close();
            inputChannel.Close();
            binding.Close();

Store and Forward Messaging Hello World, the Server:

Let’s look at the code needed to respond to the devices requests.

Create a new Windows Console Application,

To start you need to add a reference to the device EMAIl transport Microsoft.ServiceModel.Mail.dll and Microsoft.ServiceModel.Channels.Mail.ExchangeWebService.dll

Next lets add the needed using statements.

using System.ServiceModel; using System.ServiceModel.Channels; using Microsoft.ServiceModel.Channels.Mail.ExchangeWebService; using Microsoft.ServiceModel.Channels.Mail; using System.Runtime.Serialization; using System.Xml; using System.Xml.Serialization;

We need the TransmittedObject and XmlSerializerWrapper class from the device code above, copy and paste that section to your new project.

Next let’s set a few global variables from the device project.

            string serverAddress = "ServerMailAddress@ExchangServer.com";
            string serverPWD = "MyPassword";
            string clientAddress = "DeviceMailAddress@ExchangServer.com";
            string exchageServerLocation = "http://ExchagneServer";

Let now create the listener, again the input channel will block so in your production code you should spin up a new thread for each listener.  In this sample as with the device sample we will place it in line. If you are using windows credentials just pass null value as the second argument to the ExchangeWebServiceMailBinding.

            ExchangeWebServiceMailBinding binding = new ExchangeWebServiceMailBinding(
                                            new Uri(exchageServerLocation),
                                            new System.Net.NetworkCredential(serverAddress, serverPWD));
            BindingParameterCollection parameters = new BindingParameterCollection();

            IChannelListener<IInputChannel> listner = binding.BuildChannelListener<IInputChannel>
                                                  (MailUriHelper.CreateUri(channelName, ""), parameters);
            listner.Open();
            IInputChannel inputChannel = listner.AcceptChannel();
            inputChannel.Open(TimeSpan.MaxValue);
            Console.WriteLine("Channel Open");
            Message reply = inputChannel.Receive(TimeSpan.MaxValue);
            Console.WriteLine("Message Received");
            XmlSerializerWrapper wrapper = new XmlSerializerWrapper(typeof(TransmittedObject));
            TransmittedObject to = reply.GetBody<TransmittedObject>(wrapper);

Processing the message.

            to.str = to.str + " World";
            to.i = to.i + 1;

            Console.WriteLine("Responce: " + to.str + " " + to.i.ToString());

Sending the response through an output channel.

            Message m = Message.CreateMessage(binding.MessageVersion, "urn:test", to, wrapper);

            IChannelFactory<IOutputChannel> channelFactory = binding.BuildChannelFactory<IOutputChannel>(parameters);

            channelFactory.Open();

            IOutputChannel outChannel = channelFactory.CreateChannel(new EndpointAddress(
                                                MailUriHelper.CreateUri(channelName, clientAddress)));
            outChannel.Open();

            Console.WriteLine("Out Channel Open");

            outChannel.Send(m);

            Console.WriteLine("Message Sent");

Finally lets clean up the EMAIL channel objects.

            outChannel.Close();
            channelFactory.Close();

            listner.Close();
            inputChannel.Close();
            binding.Close();

Time to give it a try, start the server first then run the client code to the emulator you configured to sync with your exchange server.

Introduction to Windows Communication Foundation for the .NET Compact Framework Messaging Stack

This BLOG continues my discussion into new features of .NET Compact Framework 3.5 by diving into Windows Communication Foundation for the .NET Compact Framework Messaging stack.  To start I suggest you read Romans WCF overview BLOG at http://blogs.msdn.com/romanbat/archive/2006/10/21/windows-communication-foundation-compact-edition-and-the-story-of-the-lunch-launcher.aspx

As with all features included in NETCF, the WCF implementation needed to be small to fit into device ROM's.  Our original budget was 250kb and we tracked closely to this goal by cutting out the service layer and implementing a sub-set of the messaging stack.  Throughout our investigation a few critical end to end solutions required messaging level security, which was not orrginaly planned for.  To address these solutions, WCF for NETCF includes a subset of WS-Security.  This drove our size up to ~450kb.  The final implementation of WCF for NETCF includes the HTTP and email based transport, the text encoders and WS-Security.  Each of these features are extensible and  provides a flexible framework to allow each component to be swapped out or extended as needed. 

To introduce the messaging stack lets look at a hello world example using  HTTP or HTTPS request response.  There are distinct tasks which need to be completed for the client solution which includes; create the message, create the channel, open the channel, send the message then receive the response.   Server tasks follow similar steps; create the channel, open the channel and wait for response, parse and respond to the message.  To demonstrate this type of messaging channel I've added a simple code example below.  The client code will run on the PC so I if you do not have Orcas Beta1 or greater, which includes System.ServiceModel.dll, you can start your investigation PC them move to device when Orcas Beta2 is released.  Lets look at the specifics of a simple hello world example.

I've updated the code below to reflect the changes found in Orcas Beta2.

 Messaging Hello World Client Code:

The Hello World example below describes a simple use of the messaging stack in conjunction with the canned HTTP binding element.  

We need to implement infrastructure used by both the client and server.

First we need to add the needed references.

First add references to:

1. System.ServiceModel.dll

2. System.XML.dll

3 System.Runtime.Serialization.dll

Next we add the using statements.

using System.ServiceModel; using System.ServiceModel.Description; using System.ServiceModel.Channels; using System.Xml; using System.Xml.Serialization; using System.Runtime.Serialization;

Next we need serializable class to contain our data. 

[System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://Microsoft.ServiceModel.Samples")] public class TransmittedObject { [System.Xml.Serialization.XmlElementAttribute(Order = 0)] public string str; [System.Xml.Serialization.XmlElementAttribute(Order = 1)] public int i; }

We next need to a helper class by extend the XMLSerilizer to be used in the context of WCF messages.

public sealed class XmlSerializerWrapper : XmlObjectSerializer { XmlSerializer serializer; string defaultNS; Type objectType; public XmlSerializerWrapper(Type type) : this(type, null, null) { } public XmlSerializerWrapper(Type type, string name, string ns) { this.objectType = type; if (!String.IsNullOrEmpty(ns)) { this.defaultNS = ns; this.serializer = new XmlSerializer(type, ns); } else { this.defaultNS = ""; this.serializer = new XmlSerializer(type); } } public override bool IsStartObject(XmlDictionaryReader reader) { throw new NotImplementedException(); } public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName) { throw new NotImplementedException(); } public override void WriteEndObject(XmlDictionaryWriter writer) { throw new NotImplementedException(); } public override void WriteObjectContent(XmlDictionaryWriter writer, object graph) { throw new NotImplementedException(); } public override void WriteStartObject(XmlDictionaryWriter writer, object graph) { throw new NotImplementedException(); } public override void WriteObject(XmlDictionaryWriter writer, object graph) { this.serializer.Serialize(writer, graph); } public override object ReadObject(XmlDictionaryReader reader) { string readersNS; readersNS = (String.IsNullOrEmpty(reader.NamespaceURI)) ? "" : reader.NamespaceURI; if (String.Compare(this.defaultNS, readersNS) != 0) { this.serializer = new XmlSerializer(this.objectType, readersNS); this.defaultNS = readersNS; } return (this.serializer.Deserialize(reader)); } }

We now have enough infrastructure to build the message.  It can look something like:

            TransmittedObject to = new TransmittedObject();
            to.str = "Hello";
            to.i = 5;

            XmlSerializerWrapper wrapper = 
                new XmlSerializerWrapper(typeof(TransmittedObject));

            Message m = Message.CreateMessage
                (MessageVersion.Soap11, "urn:test", to, wrapper);

Next up we need to create the channel using the BasicHttpBinding.

            BasicHttpBinding binding = new BasicHttpBinding();

            BindingParameterCollection parameters = 
                new BindingParameterCollection();

            IChannelFactory<IRequestChannel> channelFactory = 
                binding.BuildChannelFactory<IRequestChannel>(parameters);

            channelFactory.Open();

The channel then needs to be opened.  For PC to PC you can use LocalHost. If your using a device, you will need to setup an IIS server with an appropriate address which is reachable by the device

            IRequestChannel outChannel = 
                channelFactory.CreateChannel
                (new EndpointAddress(new Uri("http://<<ServerAddress>>:<<PortNumber>>")));

            outChannel.Open(TimeSpan.MaxValue);

It time to send the message and wait for the reply

            Message reply = outChannel.Request(m, TimeSpan.MaxValue);

Once the reply is received do Deserialize it and do something with the data.

            TransmittedObject to1 = 
                reply.GetBody<TransmittedObject>
                (new XmlSerializerWrapper(typeof(TransmittedObject)));

            MessageBox.Show(to1.str + " " + to1.i.ToString());

Lets clean up

            outChannel.Close();
            channelFactory.Close();

Messaging Hello World Server Code:

Now lets look at what the server code could look like.   

The server needs all the infrastructure described above including reference and using statements, TransmittedObject and XmlSerializerWrapper

Next the channel needs to be built.

            BasicHttpBinding binding = new BasicHttpBinding();

            BindingParameterCollection parameters = 
                new BindingParameterCollection();

            IChannelListener<IReplyChannel> listener = 
                binding.BuildChannelListener<IReplyChannel>
                (new Uri("http://<<ServerAddress>>:<<PortNumber>>"), parameters);

            listener.Open();

            XmlSerializerWrapper wrapper = 
                new XmlSerializerWrapper(typeof(TransmittedObject));

Next the channel needs to be opened.

            IReplyChannel channel = listener.AcceptChannel();
            channel.Open(TimeSpan.MaxValue);

Receiving the message

            RequestContext r = 
                channel.ReceiveRequest(TimeSpan.MaxValue);

            TransmittedObject to = 
                r.RequestMessage.GetBody<TransmittedObject>(wrapper);

Process the message

            to.str = to.str + " World";
            to.i = to.i + 1;

Finally we create the message and send the response.

            Message m = Message.CreateMessage
                (MessageVersion.Soap11, "urn:test", to, wrapper);

            r.Reply(m, TimeSpan.MaxValue);

Lets Clean up

channel.Close();

I hope this provides a brief description of using the WCF messaging stack as its implemented for the device using the .NET Compact Framework.

.NET Compact Framework 2.0 SP2 Released

Microsoft .NET Compact Framework version 2.0 SP2 release is completed and is in the process of being released.  This service pack was driven customer feedback including improvements in stability, adds new heap tracking feature to the Remote Performance Monitor.

The .NET Compact Framework will be delivered to customers through various channels in the next few months.  Each channel and location will be reported on here.

Release Channels

(The links below will be updated as each release goes live)

Release Location

Web Download
http://www.microsoft.com/downloads/details.aspx?FamilyID=aea55f2f-07b5-4a8c-8a44-b4e1b196d5c0&displaylang=en

WCE 4.2 QFE

WCE 5.0 QFE

WCE 6.0 QFE

Visual Studio 2005 Patch

New Features:
Service Pack 2 of the .Net Compact Framework V2.0 includes new features in the Remote Performance Monitor aimed at finding memory leaks in the managed heap. These features allow you to take snapshots of the GC heap at any point in time and view the relationships between the live object instances in the heap. You can also compare multiple snapshots over time in order to spot allocation trends in your application as it executes.


Fixed Bugs:

  • NETCFRPM fails on x64
  • Setup install/uninstall fails silently when the MSI is launched from Control Panel-Programs and Features on Vista
  • Finalizers fail on RTF objects because COM bindings are already disposed
  • VS crashes on trying to attach without setting the Attach Enabled Registry Key
  • Thread.Join() fails with ERROR_INVALID_HANDLE on CE 6.0 platform
  • Potential memory corruption caused by circular reference
  • JIT assertion failure when non-existent COM port is addressed
  • TypeLoadException using generics with NETCF 2.0
  • IrDA is broken on Windows CE 5.0 devices
  • NetCFRPM and MDBG cannot target headless devices
  • SerialPort: Data corruption occurs if DataReceived event is used to receive Unicode characters sent across serial ports
  • SerialPort: Cannot open a COM port beyond COM9
  • SerialPort: GetPortNames() does not return serial port names beyond COM9
  • NETCF deadlocks on exit if native callback delegate has been called on native thread
  • VS 2005 RTM attempts to deploy NETCFv2.wce5.ARMV4I.cab/System_SR_ENU.cab instead of NETCFv2.wm.ARMV4I.cab/System_SR_ENU_wm.cab on Windows Mobile 6 platforms
  • XmlSerializerializationWriter: When GetSpecifiedMember returns false serialization is halted resulting in loss of data
  • Access violation marshaling a class with a string field
  • Stepping out from a Breakpoint after Func eval causes breakpoint to remain at same place and then VS 2005 hangs
  • COM: Access violation in N->M byref marshaling
  • Native exception in marshalling code when using Interlocked.Exchange
  • Access violation in StubPolicyAlloc (eestub\policy.cpp
  • SerialPort.Open thows IOException on CE 6.0 devices
  • Type.GetDefaultMembers() doesn't return base type's default members
  • Installing multiple locales of same MSI results in multiple instances of NetCF showing up in Add Remove Programs
  • VS 2005 attempts to deploy System_SR_ENU.cab instead of System_SR_ENU_wm.cab on Windows Mobile 6 platforms
  • Debugger does not correctly handle new native threads entering through COM
  • NETCFRPM parses connection string improperly when device uses ipv6
  • V2 SP2: HttpWebRequest: HTTPS request fails when TLS_RSA_WITH_3DES_EDE_CBC_SHA cipher is used
  • Debugger may AV if breakpoints active before F5
  • WebBrowser's NavigatingEventArgs does not allow to cancel navigation
  • XmlSerializer fails to deserialize enum's with spaces

Top of page

Whats new with .NET Compact Framework 3.5

It’s been a while since I’ve last posted a BLOG entry.  I’ve been busy working with my feature teams to complete .NET Compact Framework v3.5.  The .NET Compact Framework v3.5 will be shipped with the next version of Visual Studio, code name Orcas, later this year.  NETCF teams have been working hard to get all of our new features in for Orcas Beta1.  We wanted to make sure you all have an opptunity to try these new features early in the cycle to allow time to address your feedback.  Watch my BLOG or the NETCF team BLOG for release deliverables.  (NETCF Team BLOG at http://blogs.msdn.com/netcfteam/)

 

The features I’ve been working on include, Windows Communication Foundation for the .NET Compact Framework,  .NET Compact Framework Language Integrated Query, Sound and updates to GUI.  I will be describing the new features for each of these areas in future BLOGS. 

 

Lets off with a qucik look at the new Sound API's.  Last year we looked at the current managed sound API SoundPlayer.  We liked the programming model; it was simple and easy to use.  It was however tied to PlaySound which only allowed for one sound to be played at a time.  Devices on the other hand includes WaveOut which allows for hardware mixing of sounds which was desired for those who want to create a simple game.  Our solution is to use the managed SoundPlayer API’s unchanged, but deliver the sound to WaveOut allowing more than one sound to be rendered through SoundPlayer at a time.  SoundPlayer has been included in the Orcas January CTP and on, to try it grab the latest Orcas CTP.  Create a new Windows Mobile PocketPC 2003, .NET Compact Framework 3.5 project.  Then drop three buttons on the form and copy the code below to each button.  Deploy project to the emulator and give it a try. 

 

using System.Media;

 ...

private void button1_Click(object sender, EventArgs e)

{

SoundPlayer s = new SoundPlayer("\\Windows\\Windows Default .wav");

s.Play();

}

private void button2_Click(object sender, EventArgs e)

{

SoundPlayer s = new SoundPlayer("\\Windows\\type.wav");

s.Play();

}

private void button3_Click(object sender, EventArgs e)

{

SystemSounds.Exclamation.Play();

}

Developing .NET Compact Framework applications for Windows CE 4.2 Devices.

.NET Compact Framework v2.0 SP1 includes support for Windows CE 4.2 based devices.  To date, Visual Studio 2005 does not provide managed 4.2 projects nor is there any support offerings for developing solutions targeted at 4.2 devices.  The good news is the 5.0 projects work just fine with one exception, debugging is currently broken on x86 based devices.  Below I outline the steps in creating and debugging a .NET Compact Framework v2.0 SP1 Hello World application.

Using Visual Studio

The steps below outline how to create and debug a hello world application using Visual Studio 2005.  If your Windows CE device uses an x86 processor, see Using Command Line section.

1. Configuring the Development Environment

To successfully run and debug applications based on  .NET Compact Framework 2.0 SP1 you will need to apply .NET Compact Framework 2.0 SP1 to your development environment.  To update Visual Studio 2005 download the latest .NET Compact Framework 2.0 SP1 patch from http://www.microsoft.com/downloads/details.aspx?FamilyID=7befd787-9b5e-40c6-8d10-d3a43e5856b2&displaylang=en

2. Creating the Hello World Project

  1. Open Visual Studio 2005 and create a new C# Windows CE 5.0 project by selecting Device Application from the New Project dialog.
  2. From the ToolBox drop a button on the form and add code below
  3. Add Code to button click MessageBox.Show("Hello World");
  4. The Windows CE 5.0 project will attempt to deploy the .NET Compact Framework 2.0 SP1 CAB files which are not designed for Windows CE 4.2.  To disable deployment turn off Deploy the latest version of NETCF click on your projects properties by selecting the Property Menu and clicking on Properties.  Then click on the Devices tab and uncheck Deploy the latest version of the .NET Compact Framework.
  5. Build the Hello World application by selecting Build Solution under the Build menu.

3. Setting up the Device

There are two ways to get NETCF v2 SP1 included on your 4.2 device, included in ROM by building ROM images using Platform Builder 4.2 or installed into RAM by deploying the .NET Compact Framework CAB file.  See below for details

Including .NET Compact Framework v2.0 sp1 in ROM

  1. On the PC that includes Platform Builder apply the .NET Compact Framework 2.0 SP1 Update.  For more information on the Platform Builder Update, see Windows CE Download Center
  2. Once the Update is applied, open the Workspace which contains the image to be updated. 
  3. Remove .NET Compact Framework v1 from the list of included features
  4. Add .NET Compact Framework v2.0 SP1 from the Catalog.

Including .NET Compact Framework v2.0 sp1 in RAM

  1. Copy the matching CAB file from \Program Files\Microsoft Visual Studio 8\SmartDevices\SDK\CompactFramework\2.0\v2.0\WindowsCE\wce400\<<Processor>>\NETCFv2.wce4.<<proc>>.cab to your device
  2. Run CAB file on the device to install.

4. Connecting the Device to Visual Studio 2005

If your device connects to the PC using ActiveSync then make that connection now.  To connect a WindowsCE device to Visual Studio 2005 without ActiveSync see:  http://msdn2.microsoft.com/en-us/library/ms228708.aspx

5. Deploy and Debug Hello World

  1. In your Hello World application set a breakpoint at the messagebox.
  2. Type F5 to deploy.
  3. The code will stop at the breakpoint, type F5 again to continue to run the project.

Using Command Line To Debug

For x86 based devices, debugging will need to be done from the command line using mdbg.  For more information on command line debugging see David Klines blog posting at: http://blogs.msdn.com/davidklinems/archive/2006/07/13/665023.aspx

.NET Compact Framework v2.0 Workaround for CAB Error #4

A few weeks ago I BLOG’ed about a .NET Compact Framework v2.0 CAB setup error being seen on some low memory Windows Mobile 5.0 devices.  I’m happy to report the workaround for the system failure, which will be included in SP1, has now also been slipstreamed into the current download.  If .NET Compact Framework v2 CAB installation on a Windows Mobile 5.0 device fails with error #4 then this fix is for you.  Just re-download the NETCFv2 package and re-install! 

 

NETCF v2 download package: http://www.microsoft.com/downloads/details.aspx?familyid=9655156b-356b-4a2c-857c-e62f50ae9a55&displaylang=en

 

I expect to see the other languages downloaded sites to be updated as the localization pages are completed in the next few weeks.

Direct3D Mobile on Windows Mobile 5.0 Devices

I've been testing some of the retail Windows Mobile 5.0 devices 3D performance. I have found when the proper driver is included with the device, the performance is good enough to successfully develop and run a casual game.  However these drivers do vary from device to device and some care will need to be taken to accommodate each.  

Devices running on the XScale chip without a dedicated GPU have remarkably quick 3D performance due to a good software driver.  The performance is directly related to the speed of the processor.  I ran my tests on a XScale device with a processor running at 400mhz.  This class of device provides enough throughput to run a game like PocketJongg and runs sprite based games at an acceptable 30 frames per second. 

The Dell x50v and x51v include the Intel 2700G GPU. This GPU provides great performance, but comes with a cost.  Lighting performance can be substandard to the point where its not usable. If you're going to use lighting test on the x50v or x51v first.  To see a bit of code that demonstrates this performance just run the lighting sample included in the MSDN documents at http://msdn2.microsoft.com/en-us/library/ms181020.aspx.  There are also substantial  issues with clipping.  To see this in action run the billboard sample from the MSDN documents at http://msdn2.microsoft.com/en-us/library/ms181015.aspx.  To the Dells credit, it does run a simple sprite game at 60 frames per second and also runs PocketJongg.  If you work around its limitations, the Dell x50v and x51v provide the best render performance.

HTC has been releasing deices based on TI's OMAP chip.  Unfortunately the devices in the field today contain the D3DM reference driver, not the OMAP software driver.  The reference driver renders in seconds per frame.  Devices containing the reference driver are completely useless for running games.  This will not be the case with new OMAP based devices as this is in the process of being fixed with an OMAP optimized software driver.  We should see OMAP devices capable of playing 3D games later this spring.  There may also be ROM updates available to flash up the devices in the field around the same time.

Identification

You may need to identify the D3DM driver and configure the game accordingly.  For instance, if you detect the reference driver you may not want the game to run, or you may detect the Intel 2700 driver and not include lighting. Below is some sample code for detecting the the driver.

//---------------------------------------------------------------------
//THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
//KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//PARTICULAR PURPOSE.
//---------------------------------------------------------------------

using System;
using System.Windows.Forms;
using Microsoft.WindowsMobile.DirectX;
using Microsoft.WindowsMobile.DirectX.Direct3D;


namespace DisplayD3DMDriver
{
    class Class1 : Form
    {

    static void Main()
    {
        try
        {
            AdapterListCollection al = Manager.Adapters;
            string driver = al.Default.Information.DriverName.ToString();

            string message;

            switch (driver)
            {
                case "d3dmref.dll":
                    message = "Software Reference Driver";
                    break;
                case "D3DM TI3D Driver":
                    message = "OMAP Software Driver";
                    break;
                case "PowerVR D3DMobile HAL":
                    message = "PowerVR GPU";
                    break;
                case "D3DMXSC50PB.DLL":
                    message = "XScale Software Driver";
                    break;
                default:
                    message = driver;
                    break;
             }

                MessageBox.Show(message);

        }
        catch (Exception e)
        {
            MessageBox.Show(e.ToString());
        }

    }

    }
}

.NET Compact Framework v2 SP1 (Setup and Headless)

 

This BLOG started out as a way for me to describe managed game development on devices. I'm responsible for GUI and MD3DM as noted in my introduction.  I also cover setup and release which I will begin to include in this BLOG as well.

The .NET Compact Framework (NETCF) team has been working on our version 2 Service Pack 1 release which we expect to ship late this spring.  SP1 includes a few new tools to help developers create and debug NETCF applications and fixes for bugs identified both internally and externally.  This BLOG entry will cover changes to Setup and Headless.

Setup

We have seen many reports of the NETCF v2 CAB files failing to install on Windows Mobile 5.0 devices.  Some of these setup failures have been caused by a bug in the Windows Mobile 5.0 CAB installer (wceload).  If the CAB contains an .exe the installer scans the system for all running exes and in doing so takes up more memory then needed.  If your device is already low on RAM the installer fails with out an error.  This bug has been fixed in later builds of Windows Mobile 5.0 (AKU 2.2) so devices shipping in the future will not suffer form this issue. If you encounter a NETCF CAB file setup failure with error #4 you have likely hit this bug.  To work around this bug we will be re-naming all executables (cgacutil.exe) extensions to something other then .exe.  Then during installation our setup.dll will rename the file back to .exe.  To get NETCF v2 RTM installed, soft reset your device, shut down all running applications, and try running the CAB file again.

For a great description of NETCF CAB story see Ilya's post on the .NET Compact Framework team blog at: http://blogs.msdn.com/netcfteam/archive/2005/10/11/479793.aspx

Headless

We have been working on addressing the needs of Headless devices (No display driver) for some time.  We were unable to include these features with our v2 release but they are slated to be included in SP1.  Headless features will be made available to WinCE 5.0 as part of the (NETCF) v2.0 SP1 QFE sometime after SP1.  This QFE will add a new catalog item .NET Compact Framework (Headless).  Dependencies have been changed to allow NETCF to be built into a WinCE 5.0 image which does not include a display driver. We have added an error log to help with unhandled exceptions is provided.

Managed Sprite Sample and 2D Game Whitepapers for Devices

We on the NETCF team have been busy completing our v2 product, which has kept me away from my BLOG.  I have found some time to get you all up to speed on whats been happening in the Managed Mobile gaming space.

We had four great articles published describing how to create 2D games using Winforms.  These are based on games developed by the NETCF team as part of our application building exercise we run throughout the product cycle.  

Pocket Jack: Writing a Card-Playing Application
Summary:
This article describes how to create and use a card game for Smartphones. The description is illustrated by the development of a blackjack game for Smartphone.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/pocketjack.asp

Focus Point: An Image Scaling Game for Smartphones
Summary:
This article discusses how to manage image animation, scaling, and loading on Smartphones. The description is illustrated by the development of an image scaling game for Smartphone.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/focuspoint.asp

Pocket Bots: Writing a Battle Game for Smartphones
Summary:
This article describes how to generate randomly tiled landscapes, fire projectiles, detect collisions, and perform frame-based animation on Smartphones. A battle game illustrates how to develop these features for Smartphones.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/pocketbots.asp

StarLight: Writing a Space-Shooter Game for Smartphones
Summary:
This article describes how to generate moving star fields, animated sprites, and scripted movements for game objects. A space shooter game illustrates how to develop these features for Smartphones.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/starlight.asp

I’ve been porting the code samples from Beginning .NET Game Programming in C# by David Weller to the PocketPC to fully understand how you all will port your games from PC to the device.  Recently I’ve been working through his Sprite sample, which leads me to a slightly different sample for the pocketpc.  My sample uses the donut bmp which contains the sprite animation which I loop through to animate.  Next I give the sprite an x and y velocity to move it across the screen.  I detect collision with the end of the screen, once detected, I reverse said velocity.  Finally use the dpad to modify the x and y velocity.  Simple, but hope it illustrates using Sprites on device.  

Currently I’m using a pre-release build for the Dell Axim x50v, which includes hardware acceleration! More information on the x50v running Windows Mobile 5.0 see Dells x50v page.  It runs my sample quite well.  Unfortunately it has still not been released by Dell, so for the time being, you will have to be content with the emulator shipping in VS 2005 in Beta2 and in the latest CTP’s.  D3D performance with the emulator is very slow. It ships with the reference driver so it runs, ok more like walks slowly.

A few interesting observations before we get into the code.  Setting an alpha color on a bmp is not as easy as it seems.  The alpha color can be set in the TextureLoader.FromStream, colorKey parameter with a load of other parameters most of which you will want to set to their default.  Unfortunately our documentation does not include the defaults so I will list them here:

Parameter      Default
readBytes        D3DX.Default
width D3DX.Default
height              D3DX.Default
mipLevels D3DX.Default
usage               Usage.None
format             Format.Unknown
 pool Pool.VideoMemory
filter                (Filter)D3DX.Default
mipFilter          (Filter)D3DX.Default
colorKey         0

Once I identified the defaults to TextureLoader.FromSteam it was not too difficult to complete the sample using the MD3DM samples included in VS. 

If you have Visual Studio 2005 Beta2 (or one of the CTP’s) and the Windows Mobile 5.0 SDK installed you can build my sample by following my steps below. 

1. Create new blank PocketPC project
2. Add references to Microsoft.WindowsMobile.DirectX, System.Drawing, System.Windows.Forms
3. From the PPC SDK add the donuts.bmp to the project and set its Build Action to Embedded Resource to included it in the assembly
4. Add a new class named TitleSet which will contain data describing the sprite titles.  Add code:

//---------------------------------------------------------------------
//THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
//KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//PARTICULAR PURPOSE.
//---------------------------------------------------------------------
using System;
using Microsoft.WindowsMobile.DirectX;
using Microsoft.WindowsMobile.DirectX.Direct3D;
namespace SpaceDonuts {
	public class TileSet {
		private Texture texture;

		public Texture Texture {
			get {
				return texture;
			}
		}
		private int xOrigin;

		public int XOrigin {
			get {
				return xOrigin;
			}
		}
		private int yOrigin;

		public int YOrigin {
			get {
				return yOrigin;
			}
		} 
		private int numberFrameRows;

		public int NumberFrameRows {
			get {
				return numberFrameRows;
			}
		}
		private int numberFrameColumns;

		public int NumberFrameColumns {
			get {
				return numberFrameColumns;
			}
		}
		private int xExtent;

		public int ExtentX {
			get {
				return xExtent;
			}
		}

		private int yExtent;
		public int ExtentY {
			get {
				return yExtent;
			}
		}

		public TileSet(Texture tex, int StartX, int StartY, int RowCount, int ColumnCount, int xWidth, int yHeight) {
			xOrigin = StartX;
			yOrigin = StartY;
			xExtent = xWidth;
			yExtent = yHeight;
			numberFrameRows = RowCount;
			numberFrameColumns = ColumnCount;
			texture = tex;
		}
	}
}

5. Next add a new code file and add the game code below:
//---------------------------------------------------------------------
//THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
//KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//PARTICULAR PURPOSE.
//---------------------------------------------------------------------

using System;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.WindowsMobile.DirectX;
using Microsoft.WindowsMobile.DirectX.Direct3D;
using System.Runtime.InteropServices;
using System.Reflection;

namespace SpaceDonuts
{
    // The main class for this sample
    public class CreateDevice : Form
    {
        // Our global variables for this project
        Device device = null;
        Texture texture = null;
        Rectangle tilePosition;
        TileSet tileSet = null;
        Sprite sprite;
        Int16 countX = 0;
        Int16 countY = 0;
        Vector3 spritePosition = new Vector3(200, 200, 0);
        Vector3 spriteCenter = new Vector3(16, 16, 0);
        int spriteVelocityX = 1;
        int spriteVelocityY = 1;
        

        public CreateDevice()
        {
            // Set the caption
            this.Text = "Sprite Sample";
            this.MinimizeBox = false;
            this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.CreateDevice_KeyDown);
        }

        // Prepare the rendering device
        public bool InitializeGraphics()
        {
            try
            {
                // Now let's setup our D3D parameters
                PresentParameters presentParams = new PresentParameters();

                // Causes the display to appear in a window rather than
                // full screen
                presentParams.Windowed = true;

                // When a new frame is swapped to the front buffer
                // the old frame will be discarded
                presentParams.SwapEffect = SwapEffect.Discard;

                // 0 gives us the first monitor adapter on the system
                // for md3dm you must use the default device type
                // this refers to the form which will provide the rendering
                // area createflags allows various options to be set
                // present parameters set various details of how the image 
                // will be displayed within render area and what buffers and
                // formats it will have
                device = new Device(0, DeviceType.Default, this, CreateFlags.None, presentParams);
                device.DeviceReset += new System.EventHandler(
                      this.OnResetDevice);
                this.OnResetDevice(device, null);
            }
            catch (DirectXException)
            {
                return false;
            }
            return true;
        }

        void OnResetDevice(object sender, EventArgs e)
        {
            Device dev = (Device)sender;
            //create sprite object on device
            sprite = new Sprite(dev);
            //get the donuts resource out of the assembly and assign to texture
            //Default parameters for TextureLoader.FromStream (device, stream, D3DX.Default, D3DX.Default, D3DX.Default, D3DX.Default, Usage.None, Format.Unknown, Pool.VideoMemory,(Filter)D3DX.Default, (Filter)D3DX.Default, 0)
            //Load texture from the resource and set the color black as the alpha blended color
            texture = TextureLoader.FromStream(dev, Assembly.GetExecutingAssembly().GetManifestResourceStream("Sprites.donuts.bmp"), D3DX.Default, D3DX.Default, D3DX.Default, D3DX.Default, Usage.None, Format.Unknown, Pool.VideoMemory, (Filter)D3DX.Default, (Filter)D3DX.Default, Color.Black.ToArgb());
            //configure TileSet to the size of tile in bmp
            tileSet = new TileSet(texture, 0, 0, 6, 5, 32, 32);
            //set tileposition including the offset to the center of the sprite
            tilePosition = new Rectangle(tileSet.XOrigin, tileSet.YOrigin, tileSet.ExtentX * 2, tileSet.ExtentY * 2);
        }

        // All rendering for each frame occurs here
        private void Render()
        {

                //Clear the backbuffer to a blue color 
                device.Clear(ClearFlags.Target, System.Drawing.Color.BlueViolet,
                    1.0f, 0);

                //Begin the scene
                device.BeginScene();

                sprite.Begin(SpriteFlags.AlphaBlend);
                //draw Sprite at current position. 
                sprite.Draw(tileSet.Texture, tilePosition, spriteCenter, spritePosition,Color.White.ToArgb());
                sprite.End();

             
                //End the scene
                device.EndScene();
                device.Present();
                
                //rotateSprite moves image to the next tile
                rotateSprite();
                //moveSprite to change the sprite position for the next render.
                moveSprite();

                //Application.DoEvents();
         }
        

        // Called to repaint the window
        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            // Render on painting
            this.Render();

            // Render again
            this.Invalidate();
        }

        // Called to repaint the window background
        protected override void OnPaintBackground(
            System.Windows.Forms.PaintEventArgs e)
        {
            // Do nothing to ensure that the rendering area is not overdrawn
        }


        static void Main()
        {
            CreateDevice frm = new CreateDevice();

            // Initialize Direct3D
            if (!frm.InitializeGraphics())
            {
                MessageBox.Show("Could not initialize Direct3D. " +
                    "This tutorial will exit.");
                return;
            }

            Application.Run(frm);
        }

        private void CreateDevice_KeyDown(object sender, KeyEventArgs e)
        {
            //use rocker to modify velocity 
            if ((e.KeyCode == System.Windows.Forms.Keys.Up))
            {
                // Rocker Up
                // Up
                spriteVelocityY -= 1;
            }
            if ((e.KeyCode == System.Windows.Forms.Keys.Down))
            {
                // Rocker Down
                // Down
                spriteVelocityY += 1;
                
            }
            if ((e.KeyCode == System.Windows.Forms.Keys.Left))
            {
                // Left
                spriteVelocityX -= 1;
            }
            if ((e.KeyCode == System.Windows.Forms.Keys.Right))
            {
                // Right
                spriteVelocityX += 1;
                
            }

        }

        private void moveSprite()
        {
            if (spritePosition.X > (this.Width - (tileSet.ExtentX * 2)) || spritePosition.X < 0)
            {
                //if sprite hits either left or right side of screen provide reciprocal velocity   
                spriteVelocityX *= -1;
            }


            if (spritePosition.Y > (this.Height - tileSet.ExtentY * 2) || spritePosition.Y < 0)
            {
                //if sprite hits either top or bottom of screen provide reciprocal velocity     
                spriteVelocityY *= -1;
     
            }

            //advance sprite by adding to its position the velocity            
            spritePosition.X += spriteVelocityX;
            spritePosition.Y += spriteVelocityY;
        }

        private void rotateSprite()
        {
            //change the count to move to the next sprite tile
            if (++countX > 4)
            {
                countX = 0;
                if (++countY > 5)
                {
                    countY = 0;
                }
            }
            //set coodinates for current tile
            tilePosition.X = tileSet.XOrigin + (countX * tileSet.ExtentX * 2);
            tilePosition.Y = tileSet.YOrigin + (countY * tileSet.ExtentY * 2);
        }


    }
}

6. Compile and deploy to the emulator or device if you have it.  Move the donut around the screen with the d-pad, watch it hit the side of the screen and bounce off.

Introduction

Hi All,

I thought I would introduce myself to the BLOG-O-Sphere, my name is Mark Prentice and I’m a Program Manager for the .NET Compact Framework (NETCF) team. My responsibilities on the NETCF team include help manage GUI and the Managed D3D for Devices (MD3DM) effort. This BLOG will cover my journey of using these new interfaces to create and help others create games for devices.

First a little bit about me. I’ve been at Microsoft for 12 years now. For the first 10 years I worked in and around VBA and Office. Some of this time was spent in Access support and the remainder as a PM for the Office Developer team. I wrote a few free Access wizards, helped a few versions MOD ship and finally drove the Web Services Reference tool for VBA. I then went on to help lay some of the ground work for the new Visual Studio Tools for Office, which I think is the great step in bring modern programming language to Office based solutions. After 10 years of working with developers in Office, I was up for a change. So I then jumped to the NETCF team to help ship V1 Sp3, Sp3 and help take on managed GUI development and finally to creating games on devices, which leads me back to this BLOG.

Now some background on our managed D3D wrappers. The Windows CE 5.0 team implemented a version of DirectX for devices last year. This version of DirectX was inspired by DX8 (D3DM). It is not totally compatible but close enough to give DX C++ developers a head start on creating games for devices. It was a good first step, the only problem was it only ran with software drivers on one WCE development board. Since then D3DM has been included in Windows Mobile 5.0 OS! Now when the new Smart Phones and PocketPC are delivered based on Windows Mobile 5.0 the will at a minimum contain software D3D drivers. In addition manufacturers, like Dell, will offer hardware D3D drivers. For more information on the Windows Mobile space see: http://www.microsoft.com/windowsmobile/default.mspx also see Dells announcement for the Axim x50v upgrading to WM 5.0 at: http://www1.us.dell.com/content/topics/global.aspx/corp/pressoffice/en/2004/2005_05_10_rr_000?c=us&l=en&s=corp

We on the NETCF team saw an opportunity to port the DX9 managed wrappers based on D3DM to be included in NETCF V2. Now C# and VB.NET developers can quickly create managed games for devices.

MD3DM wraps all MD3DM and provides additional mesh functionality. Below is a list of the major features in MD3DM

  • Complete access to the underlying native D3DM api
  • Fixed point support (Vertex Data, Matrices, Lights, and Materials)
  • Sprite
  • Font
  • Mesh
  • TextureLoader
  • Mesh Loader (As sample code)

Instead of my going on and on about the specifics, I suggest you go out and review some of our MVP’s articles. Casey Chestnut wrote a good introduction to MD3DM at http://www.mperfect.net/cfMDX/. He also took the next step and ported WorldWind. This work is documented at: http://www.mperfect.net/cfWorldWind/

So you want to get started? No problem. Just install Visual Studio 2005 beta2 standard installed on your test PC and the Windows Mobile 5.0 SDK, see: http://msdn.microsoft.com/mobility/windowsmobile/howto/windowsmobile5/install/default.aspx

Next on my BLOGs agenda:

  • Sprite Sample
  • Description of my port of a Sprite game
  • Device specific game loop’s
  • And much more.

Disclaimer(s): This posting is provided "AS IS" with no warranties, and confers no rights. Some of the information contained within this post may be in relation to beta software. Any and all details are subject to change.

 
Page view tracker