Sunday, December 02, 2007

Microsoft Office Mobile 6.1: Upgrade for Microsoft Office 2007 file formats

A couple of days ago Microsoft Office Mobile 6.1 was released and you can upgrade your version of Windows Mobile Office for free to version 6.1 as long as you have a WM 5 or later device and you have a previous version of Office Mobile.

This version supports the new Office 2007 file formats.

Get it here: http://www.microsoft.com/downloads/details.aspx?familyid=4b106c1f-51e2-42f0-ba32-69bb7e9a3814&displaylang=en&tm

Ensure you have ActiveSync 4.5 before installing.

Microsoft SQL Server Compact 3.5 Available

As released with Visual Studio 2008, SQL Server Compact 3.5 is now available in RTM. It is free to use as long as you're not using any type of replication or RDA (remote data access) with a back office SQL Server. Get it here.

SQL Server Compact 3.5 runs side-by-side with SQL Server 2005 Compact Edition (3.0/3.1).

Notice the name change, again ;)

SQL Server 3.5 contains some enhancements such as:

  1. Timestamp datatype - at last! ;)
  2. Transact-SQL enhancements such as:
    1. Nested query in FROM clause
    2. CROSS APPLY and OUTTER APPLY
    3. CASE and DECIMAL
    4. SET IDENTITY INSERT
    5. TOP


SQL Server Compact 3.5 runs under the .NET Compact Framework 2.0. So although it was released at the same time as Visual Studio 2008 and with Visual Studio 2008, you don't actually need VS 2008 or the .NET CF 3.5 to use it. You can use it within your Visual Studio 2005 projects today if you want to.

Read about all the new features here.

This release is supported on Pocket PC 2003 as well as the later devices, WM5, WM6.

Friday, November 30, 2007

KB936021 XML Core Services Update Breaks XSLT Transformation (which could be any BizTalk maps)

KB936021 - Microsoft Security Bulletin MS07-042 published on the 14th August 2007 which fixes the potential vulnerability of executing remote code via an XSLT file from within Microsoft Internet Explorer may break your BizTalk (all versions) implementation (or any XSLT transformation code) - Only applicable to BizTalk: but this is depending on how you have implemented your maps.

This update fixes, XML Core Services 3.0, 4.0 and version 6.0. Previously, the resolveExternals (part of the DOM interface) property was true by default allowing you to code something like the following in XSLT:-

<xsl:import href="addressbook_vv.xsl"/>

<xsl:call-template name="addressbookupdate_out_vv">
<xsl:with-param name="pdamgr" select="PDAMGR" />
</xsl:call-template>
This was possible before the fixpack because the DOM was true by default and BizTalk does not explicitly set it to true.

After installing this fixpack, the above code will not work because BizTalk uses the XML Core Services for XSLT transformation and doesn't explicitly set the resolveExternals property to true - which is what is required in order to call sub maps (sub XSLT templates within stylesheets).

But this depends on how you have written your maps in BizTalk. If you have used the BizTalk mapper or used .NET C#/VB.NET to create your maps then you will be OK. But the mapper tool is very inefficient so we have written our maps ourselfs by hand which use re-usable routines referenced as above. Now this is causing us an issue and will probebely have to be written in .NET.

UPDATE: In relation to BizTalk, Microsoft has told us there will probebly not be a hotfix in the future to allow you to set this property or force resolveExternals to true as they see this as a security risk.

If you are encountering an issue outside of BizTalk when trying to do a transformation with MS XML Core Services, then this is an easy fix. Simply set resolveExternals to true before you do the transformation.

Thursday, November 29, 2007

Anonymous Types: C# 3.0

Anonymous Types are a new feature available with C# 3.0 and VB 9. In fact one of many new features that is available with C# 3.0 or VB 9.0 (Visual Studio 2008, "Orcas"). This is a compiler feature not a framework feature. As with Implicitly Typed Local Variables, you only need the C# 3.0 or VB 9 compiler in order for your end users to use this functionality - not the .NET Framework 3.5. They go hand in hand with Implicitly Typed Local Variables. However, generally you will be using Visual Studio 2008 as who these days explicitly uses a command-line compiler to compile their code!!? Not many.

I have written an article on Implicitly Typed Local Variables, if you haven't read it yet, you can do so here.

Anonymous Types allow you to create an instance of a class without the need to write code for the class in the first place. Anonymous Types go hand in hand with LINQ and Implicitly Typed Local Variables. They must be coded in-line. Consider the following example:
new {Name="Simon Hart", Age=30, Married=True}
The above code needs to be assigned to a type. But what type? The type is anonymous! this where the other new feature of C# 3.0 known as Implicitly Typed Local Variables come into play by use of the new keyword - var.

So the above would look like the following when assigning to a Implicitly Typed Local Variable:
var person = new {Name="Simon Hart", Age=30, Married=True};
What happens when you run this code? Well the following image shows the types that have been inferred to their assumed types:
Clever stuff eh!? As you would expect the other properties; Married is of type boolean and Name is of type string.

I know what you're thinking, "why would I want to do this". Well the answer is for many reasons and in lots of places. Sometimes you create types that you are not too concerned about and the lifecyle is very short. Other times you have an designed an encapsulated type that will never be exposed to the outside world. In addition the power comes when using this with LINQ. You can use LINQ without Anonymous Types and in fact without Implicitly Typed Local Variables, but it makes your life a lot easier to use these new features. It allows you to craft you're own types at runtime, maybe based on what the user has requested. When you get to grips with LINQ, Anonymous Types are very powerful.

I will be publishing many articles on LINQ very soon..

Wednesday, November 28, 2007

Implicitly Typed Local Variables: C# 3.0

Remember var's - well if you are a native VB or a COM or even an OO COBOL developer you will have almost certainly used variants/objects. Var is a new keyword used in C# 3.0, but don't mistake it for a variant or a typeless object - it is far from it.

The var keyword allows you to define a variable whose type is implicitly inferred from the expression used to initialize the variable. The var keyword is basically a compiler trick, there is no difference in the generated IL when implicitly delcaring the var from explicitly declaring the integer. So you could code the following in C# 3.0 which is perfectly legal:
var i = 0;
After executing the above code the CLR sets the type to an integer. i is strongly typed, and treated the same as defining an integer variable. So the above is the same as the following:
int i = 0;
As you can see the var has been inferred to an integer value type:

One requirement is that the var is initialized on the same line as the var is declared and the intializer must be an expression. So you couldn't code:
var i;
Trying to compile the above code will give a compiler error: "Implicitly-typed local variables must be initialized":

You also couldn't set i to null attempting to do this will generate compiler error "Cannot assign to an implicitly-typed local variable".

So the following is invalid:
var i = null;
You also must define the var in-line. You cannot define the var outside of the member, event, property etc attempting to do this will generate compiler error "The contextual keyword 'var' may only appear within a local variable declaration":

So we have covered the basics of implicitly typed local variables and I bet you're probably thinking, what is the point of them, I mean doesn't this just make you're code more difficult to read?

Well the point of them is they go hand-in hand with LINQ and Anonymous Types. LINQ is a set of extensions to the .NET framework 3.5 that encompass Language Integrated Query set and transform operations. LINQ is available with C# 3.0 and Visual Basic 9. You will need the C# 3.0 or VB 9 compiler and .NET Framework 3.5 to use LINQ which come with Visual Studio 2008. In addition, var is generally used with Anonymous Types.

I have written an article on Anonymous Types, if you haven't read this yet, you can do so here.
I will be writing some articles on LINQ soon...As LINQ is very powerful and will make our jobs as developers easier. I can see LINQ simplifying the data layer completely. I've never liked the complexity of ADO.NET - it's too problematic which is why I have been using the enterprise library data application blocks from the pattern & practices group for ages now. LINQ is going to make data access even easier and more importantly, cleaner.

But .. I will talk more about LINQ to come. There is a lot of information on LINQ in the communities - which is great, the sooner everyone starts using it the better!

HTC Touch Cruise

A very cool device released recently by HTC named the HTC Touch Cruise: http://www.htc.com/product/03-product_htctouch_cruise.htm

It is a very high-end Windows Mobile Professional device with the following points of interest:

  • GPS chipset
  • 400 MHz Qualcomm processor
  • Windows Mobile 6 Professional
  • 3G (UMTS) support
  • 3 mega pixel class
  • 256MB ROM
  • 130g in weight
  • Built-in FM radio
  • HTC's TouchFLO interface - much like the iPhone technology
Very very high-spec.

Sunday, November 25, 2007

C# 3.0 Language Specification

Get the C# 3.0 language specification here. I was going to buy a book on the subject, but thought, what the heck I'll read the spec written straight from the engineers who designed C# as we know it.

Extension Methods: C# 3.0

A new feature in C# 3.0 is extension methods. I must say I like extension methods - very much. They allow you to extend any type transparently (without having to derive from it). The best thing is, this is not a .NET 3.5 feature, in other words it doesn't require .NET 3.5, but *does* require the C# 3.0 compiler. So if you want to implement this functionality today, your users do not require .NET 3.5.

How many times have you wanted to check a value type such as an integer for nulls or zeros with ease. Previously you would've had to write something like the following:

Note: the ? indicates a nullable generic.

int? i = 0;

if (i == null || i == 0)
//Do something


The above code is fine, but we can simplify it.

Here's a simple example of its use:

1. Define a static class, the name doesn't really matter but the class must be static.

public static class IntHelper
{
}


2. Now implement a new method as follows:


public static class IntHelper
{
public static bool IsNullOrZero(this int? i)
{
if (i == null || i == 0)
return true;
else
return false;
}
}


The method must also be static and contain the this keyword before the type.

3. Now in order to use this extension, we could code something like the following:

int? i = null;
if (i.IsNullOrZero())
Console.WriteLine("i is null");


The above code is alot cleaner than testing for zeros and nulls. Of course you could let your imagine run wild with extension methods. But I recommend only using them for simple operations as your code could soon become too complicated which would then become more difficult to maintain especially when new developers come on board.

Thursday, November 22, 2007

Where has Windows Mobile 6 SDK gone!!?

After installing Visual Studio 2008 I was unable to target Windows Mobile 6 devices from within the '08 IDE - even though I have Windows Mobile 6 SDK installed and can still compile against WM6 in Visual Studio 2005.

This is a known issue by Microsoft and is a result of installing Visual Studio 2008 and to fix, simply re-install the Windows Mobile 6 SDK.

Wednesday, November 21, 2007

Extracting Visual Studio 2008 UDF from an ISO image

So you've just downloaded Visual Studio 2008 in .iso format from MSDN subscriptions...what now? Well you can burn this image to DVD then install from there. But what if you want to just extract the .iso and install from HD?

To do this you need a UDF (Universal Disk Format) compatible extractor. You can use either ISO buster (not free) or WinRAR which supports UDF from version 3.7 and onwards.

Tuesday, November 20, 2007

Visual Studio 2008 & .NET Framework 3.5 RTM is here!

UPDATE: If you are an MSDN subscriber and have the monthly MSDN shipments, VS 2008 will only be included from Jan 2008 and onwards and not before.

Yesterday (Monday 19th November 2007) the RTM version of Microsoft Visual Studio 2008 was released. You can download this via MSDN subscriptions or a 90 day trial version if you do not have an MSDN subscription.

I am downloading VS 2008 Team Suite edition as I write this blog entry. See here for more information. It is a rather hefty download at just short of 4 GB!

I am in the process of writing a blog entry regarding what's new for device application developers with the advent of VS 2008, so watch this space...

Thursday, November 15, 2007

SQL Server CE Performance Tips

UPDATE: This is a very good article: http://www.sql-server-performance.com/tips/sql_server_ce_p1.aspx

As there have been a few questions recently in the communities regarding Microsoft SQL Server CE performance, I'd thought I'd post the following link which contains some useful information regarding SQL Server CE performance tips: http://support.microsoft.com/default.aspx/kb/274112

Also see here for information of changing the default temp table directory that SQL Server CE uses when processing transactions or large queries from RAM to a storage card which will also improve performance and reduce those annoying OutOfMemoryException exception's.

Also try using the DataReader class over the DataSet and ResultSet wherever possible if you don't need the scrollability, updateable and a bindable cursor as the DataReader will always be more efficient. I myself always use DataReader which populates a strongly typed collection which I can then use to bind to my controls where required.

You might also find this link interesting, written by Chris Tacke of OpenNETCF. It talks about data caching in SQL Server CE to improve performance.

Tuesday, November 13, 2007

Consolas Font Pack for Visual Studio 2005

Although this little gem has been released for sometime, I'd thought I'd post a link to the new font named Consolas.

Consolas is a new font created by Microsoft designed for ClearType and is intended for use in programming environments.

It is supported on XP and later. Installing the pack will set VS 2005 to use Consolas by default. Not too sure about previous versions.

Here are a couple of screen shots I created to illistrate the differences - although I admit it is very difficult to see the difference as Blogger has resized the images! I will be hosting my blog on my own server very soon once I have finished building it:

Courier New


Here is the new Consolas font:


You can download the pack from here: http://www.microsoft.com/downloads/details.aspx?familyid=22e69ae4-7e40-4807-8a86-b3d36fab68d3&displaylang=en

Wednesday, November 07, 2007

Microsoft Commits to Visual Studio 2008 RTM Release Date

Announced at the Barcelona TechEd a couple of days ago (Monday 5 Nov) by Somasegar - the vice president of the developer division at Microsoft, Microsoft has committed to releasing the RTM version of Visual Studio 2008 and the .NET Framework 3.5 by the end of November 2007.

I myself was going to re-download the VS.NET beta 2 VPC image as the old one expired prematurely on the 1st Nov but I think I'll wait until the end of the month and get the RTM version.

A CTP release of the new Microsoft Sync Framework SDK is available now from here: http://msdn.microsoft.com/sync

This framework we are currently testing looks very interesting indead. In the past we've had to write a load of plumbing code for synchronizing offline data on the device with the back office. The Sync Framework contains a set of API's that do all this complex code behind the scenes. These API's work for any protocol, data type of data store. Download and try it today. I am not sure that the Sync Framework will replace our complex sync code but it is definately an interesting solution. I will blog regarding my Microsoft Sync Framework experiences here.

Friday, November 02, 2007

Service Management Europe - Oct 24th - 25th 2007

Well last week we were an exhibitor at the Service Management Europe SME show hosted at the Birmingham NEC. I went up on the 24th (Wednesday) just to see who was there and to ensure the system worked and I was there in case of any problems arose, none of which did, thankfully ;) We used a Wi-Fi ad-hoc peer-to-peer network for communications rather than GPRS/UMTS for reliability and it worked great.

It was interesting for me to talk to other companies regarding their mobile solutions and what they are doing with it and how they are saving companies money in operation costs using their solutions. The show also had Seminars every 45 Min's or so which hosted real-world experiences from various companies that found using mobility software reduced their operation costs.

I must add to the above. Most of the people, in fact all the people I spoke to never once gave me any figures from real world clients a break down in return on investment ROI. I found this rather strange and somewhat shortsighted. If I were a decision maker looking to save in operation costs, the first thing I want to see is the ROI.

Needless to say I saw some very sophisticated systems each of which had their strengths and had their weaknesses. Remember this show was about Service management, so it only covers mobility in the service management space. But boy, is this a up and coming area - for sure no doubt about that. It reminds me of when ERP - Enterprise Resource Planning systems first came about, now today, ERP is a very competitive business and only the large companies tend to win the business. Service management is still for the taking.

I also noted 90-95% of the solutions were written for Windows Mobile/Windows CE.

A lot of companies were using GPS in their solutions, some primarily GPS, others used it in their arsenal of tools, which in my experience is the best thing to do.

I saw some very impressive job scheduling solutions which used GPS.

Overall the show tended to be a lot busier in the morning then in the afternoon. In the afternoon it got a bit quieter.

To sum up, all the solutions had their strengths and weaknesses. Putting all these strengths together for the average service provider company, you would have a very, very functional system that would deliver ROI in no-time.

Friday, October 26, 2007

Microsoft Zune Media Converter

I recently bought a Microsoft Zune which I am very pleased with. It allows me to watch movies and listen to music on boring train journeys.

However, I have lots of AVI files from years ago that I'd like to watch on the Zune. But the Zune doesn't support AVI files so a converter is required. Windows Media Encoder is great for this. You can download Windows Media Encoder free from here: http://www.microsoft.com/downloads/details.aspx?FamilyID=5691ba02-e496-465a-bba9-b2f1182cdf24&displaylang=en

If you have an Xbox 360, you can plug your Zune into the Xbox via USB and watch the movies or listen to music thorugh your TV.

Thursday, October 25, 2007

Start doing more

I just found this really cool site: http://www.startdoingmore.com/. It shows various Microsoft applications running on Windows Mobile in a really nice graphical manner. It shows a quick example of Live Search, which I have installed on my device awhile back which is really neat.

Get Windows Mobile Live Search here: http://www.microsoft.com/windowsmobile/livesearch/default.mspx

You'll need data access, Wi-Fi or GPRS/UMTS connection for it to work.

Saturday, October 20, 2007

P/Invoke made easy

I have been using http://pinvoke.net/ for quite a while now and thought I'd share this site for anyone who has never come across it before.

It allows you to interop with unmanaged code easily by selecting the dll that contains the function you wish to use and the site gives you a C# and sometimes a VB.NET signature of the function. In addition, the site has a directory of how to code the structures, interfaces and enums etc for the unmanged function.

The site supports both desktop and devices.

Sunday, October 14, 2007

Targeting both VGA and QVGA Screens

I have written some previous articles on GDI about one thing or another in the past, but never mentioned the simple practice of ensuring your code will display correctly under all screen types. Even though the code I have posted contains this practice I have never made a point of it.
It is very simple. Carry out the following steps:

1. Declare a constant that defines the points per inch you intend to design your control. In the example below, I have used 96dpi which is a QVGA screen (320x240) which is the majority of devices on the market today.


private int DESIGNPOINTSPERINCH = 96;



2. The example below shows the OnPaint method with a simple piece of code that displays "Hello World!" 20 dpi from the left and top edge of the screen.


protected override void OnPaint(PaintEventArgs e)
{
Graphics grfx = e.Graphics;
Brush foreColorBrush = new SolidBrush(Color.Black);
Font boldFont = new Font(Font.Name, 20, FontStyle.Bold);
grfx.DrawString("Hello World!",
boldFont,
foreColorBrush,
20 * grfx.DpiY / DESIGNPOINTSPERINCH,
20 * grfx.DpiY / DESIGNPOINTSPERINCH);
boldFont.Dispose();
foreColorBrush.Dispose();
grfx.Dispose();
}




Now try running the above code on a VGA and QVGA device, you'll see the position will be proportional to the screen size. This technique becomes very handy when the positioning is very important such as owner drawn lists etc.

Saturday, October 06, 2007

Books I am currently reading - Oct 07

I want to start a regular blog regarding the books that I read and to give feedback as to what I think of the book and whether I recommend it to others.

As most of you are probably aware now that .NET 3.0 has been released - formally known as WinFX in the early pre-beta days last year. But my guess is probably most shops are not using .NET 3.0 yet. Would like to hear feedback from anyone on this, experiences etc.

Part of .NET 3.0 is WCF - Windows Communication Foundation - formally known as Indigo.

Indigo is a new set of API's that build ontop of other messaging technologies, such as Enterprise Services (COM+), Web Services, MSMQ, Windows Services (NT Services), .NET Remoting. But WCF is much easier than all these and massively flexible.


I highly recommend this book. It starts by going back to fundamentals and talks about SOA's. If you're not sure what an SOA is, then this book should clear things up for you.

The book is highly technical and covers WCF to a fairly good degree. You will certainly learn enough in order to be productive in WCF once you have read this book.

The WCF fundamentals are covered throughout the book with constant examples which are the ABC's of WCF - Address, Binding and Contract.

The book covers most types of bindings - ones which come with WCF which to be honest is probebly more than enough. I like the easy reading layout, the author's writing style encourages you to read more and more rather than to put the book down.

I have also in my possession the WPF version of this book which I will blog a brief review of once I have read it. Watch this space!! ;)

Service Management Europe - Oct 24th - 25th 2007

My company will be an exhibitor at the coming Service Management Europe show at the famous NEC centre in Birmingham, England Oct 24th - 25th 2007.

Service Management Europe is an event that dedicated to the ever growing field service market. This market consists of maximizing return and reducing costs by the use of mobile technology.

My company name is: Enton Consulting and Technology who specialize in mobile technology: http://www.servicemanagement.co.uk/enton-consulting-and-technology-.html. We will be on stand 542. We will be demonstrating our latest offerings plus we will have a speaker from one of our major clients that we are working with and he will be talking how our solutions have reduced his business costs, made his business more efficient and made his whole operation more profitable.

See here for more information: http://www.servicemanagement.co.uk/.

Wednesday, October 03, 2007

Microsoft MVP Award - 2008

I have recently been awarded the 2008 MVP Visual Developer - Device Application Development award from Microsoft for my contributions to the community over the last year which has totally taken me by surprise. I have been nominated a few times in the past but never awarded an MVP. So thank you Microsoft and the people who nominated me, I am very grateful ;)

You can be sure to see me at the Global Summit in Redmond April 2008!

Sunday, September 23, 2007

Calculating the length of Text using GDI

I'd like to show a simple example of calculating the length of text which is very handy when building your own controls. This technique works with either version of the framework (desktop or compact).

Take the following control I designed below. Notice how the top line which contains the description is too long for the width of the screen space, so a "..." has been appended to the description. This makes much easier reading.


The code is very simple. Something like the following would need to be added to your OnPaint method of your user control class:



protected override void OnPaint(PaintEventArgs e)
{
Color fontColor;
int descriptionLength;
int periodsWidth;
int maxDisplayableDesc;
bool truncated;
Graphics grfx = e.Graphics;
int clientWidth =
this.ClientSize.Width - (this.VScrollBar.Visible ? this.VScrollBar.Width : 0);

//Foreach item in your user control do the following:
string description = assignmentListItem.Description;
periodsWidth =
(int)(_grfx.MeasureString("... ", boldFont).Width);
descriptionLength = (int)(grfx.MeasureString(description, boldFont).Width);
maxDisplayableDesc = clientWidth - periodsWidth;
index = description.Length;
truncated = false;
if (descriptionLength +
(DRAWX_OFFSET * _grfx.DpiX / DESIGNPOINTSPERINCH)
> maxDisplayableDesc)
truncated = true;

while (descriptionLength +
(DRAWX_OFFSET * _grfx.DpiX / DESIGNPOINTSPERINCH) > maxDisplayableDesc)
{
description = description.Substring(0, index - 1);
index--;
descriptionLength =
(int)(_grfx.MeasureString(description.Substring(0, index - 1),
boldFont).Width);
}

if (truncated)
description = description + "...";

//Draw the description.
grfx.DrawString(description,
boldFont,
foreColor,
DRAWX_OFFSET * _grfx.DpiY / DESIGNPOINTSPERINCH,
itemTop + (DRAWY_OFFSET * _grfx.DpiY / DESIGNPOINTSPERINCH));

grfx.Dispose();
}


Of course as you can see the code above is massively cutdown and only focuses on the actual calculation of whether the text will fit within the screen space and if it doesn't, how many characters can we display before we run off the edge of the screen. This is assuming you are using a non-proportional font which is what the code was designed to work with. In fact an improvement would be to check what the typeface font is before doing the character-by-character calculation.

Thursday, August 30, 2007

Halo 3 Ring Tone for Windows Mobile

I thought I would share the Halo 3 ringtone I discovered on Microsoft's web site today.

You can download this ringtone from here. Enjoy!

VS "Orcas" beta 2

Visual Studio 2008 Beta 2 has recently been made available for download here.

You can download the Team System versions as a Virtual PC image but the others are fully blown products.

This build includes beta 2 of the .NET Compact Framework 3.5.

Saturday, August 11, 2007

BizTalk XSLT Mapper Tool

I've been doing a lot of work with BizTalk lately which means I have not been doing much Windows Mobile development ;( This will change over the coming weeks and I will be back to writing "proper" code.

Anyway, if any of you have ever done any BizTalk integration you might find the BizTalk mapper not sufficient and not very efficient so you have no choice but to dive straight into XSLT.
The problem with the BizTalk mapper is it doesn't allow you to divide up your XSLT or VBScript/JavaScript to use common routines and more fundementally, it is too simplistic and not adequate for most solutions.

The problem is all versions of Visual Studio doesn't have any support for XSLT transforming or debugging. This is paramount when testing your maps for BizTalk. The company I work for has been using a tool named Xselerator by Marrowsoft for some years. It is a tool that allows you to run and debug your maps without deploying the solution into BizTalk first.

For the past 4 weeks or so I have become very fond of this tool and recomend it highly for your BizTalk mapping work.

I use Visual Studio to do the editing as I prefer the VS editor, then I use Xselerator to actually run the translation.

You can get Xselerator here: http://www.marrowsoft.co.uk/. It is not free but well worth the money. You can download a demo to try.

Saturday, July 07, 2007

"How Do I?" Videos for Devices

I just found this on MSDN today which I thought I would share as it could be useful whether you are an CF vet or a CF novice: http://msdn2.microsoft.com/en-us/netframework/bb495180.aspx

Saturday, June 30, 2007

Mobile Client Software Factory: Data Access Application Block - Part 2

This article is a second part on demonstrating a simple example using the Data Access Application Block. You have haven't read Part 1, then you can do so here: Mobile Client Software Factory: Data Access Application Block - Part 1.

As always, if you haven't already downloaded and installed the Smart Client Software Factory, you can get it here.

In this article I will build on the previous article which will demonstrate adding transaction support to the CustomersDB class. As in the last article I added a Save() method which accepted a CustomerInfo object. In this article, we will create an overloaded Save() method but this method will accept a CustomerCollection object which will hold a collection of CustomerInfo objects that need to be written to the Customers table.

1. Firstly we will need to add the CustomerCollection class. Again we would normally separate this class from the CustomersDB namespace but we have included it here for simplicity.

namespace MyCompany.DAL
{
public class CustomerCollection : CollectionBase
{
#region ctor
public CustomerCollection()
{
}
#endregion

#region Methods
public int Add(CustomerInfo customer)
{
return base.List.Add(customer);
}

public void Insert(int index, GatewayInfo gateway)
{
base.List.Insert(index, gateway);
}

public bool Contains(CustomerInfo Customer)
{
return base.List.Contains(customer);
}

public void Remove(CustomerInfo customer)
{
base.List.Remove(customer);
}
#endregion


#region Indexers
public CustomerInfo this[int index]
{
get
{
return (CustomerInfo)List[index];
}
set
{
List[index] = value;
}
}
#endregion
}
}



2. Now you'll need to add the new overloaded Save() method which accepts the collection of customer objects and wraps this process up in a transaction. There are a couple of things that has changed in the CustomersDB class in this step. The first is the Fields region. I have added a couple new variable objects. The Dispose method has also changed to ensure objects get finalized.

The code is as follows:-

using System;
using System.Collections.Generic;
using Microsoft.Practices.Mobile.DataAccess;
using System.Data.SqlServerCe;
using System.Data.Common;

namespace MyCompany.DAL
{
public class CustomersDB : IDisposable
{
#region Fields
private SqlDatabase database = null;
private DbTransaction transaction = null;
private DbCommand cmd = null;
#endregion

#region ctor
public CustomersDB()
{
database = new SqlDatabase("Data Source = \Storage Card" +
"\MyDatabase.sdf; password=mypassword;encrypt "+
"database = TRUE");
}
#endregion

#region Methods
public CustomerInfo GetCustomer(Guid guid)
{
CustomerInfo customer = null;
if (guid = Guid.Empty)
return customer;

DbCommand cmd =
database.DbProviderFactory.CreateCommand();
DbParameter pname = cmd.CreateParameter();
id.DbType = System.Data.DbType.Guid;
id.ParameterName = "@CustomerID";
id.Value = guid;

cmd.Parameters.Add(id);

//Now for the SQL.
cmd.CommandText =
"select * from Customers where CustomerID = @CustomerID";
DbDataReader reader = null;

try
{
reader = database.ExecuteReader(cmd, null);
if (reader.Read())
customer = GetCustomer(reader);
}
catch (SqlCeException ex)
{
throw ex;
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (reader != null)
reader.Close();
cmd.Dispose();
}
return customer;
}

public bool Save(ref CustomerInfo customer)
{
if (customer == null)
throw new ArgumentException
(Properties.Resources.CustMustNotBeNull);

DbCommand cmd =
database.DbProviderFactory.CreateCommand();
DbParameter id = cmd.CreateParameter();
DbParameter address = cmd.CreateParameter();
DbParameter name = cmd.CreateParameter();
DbParameter changeStamp = cmd.CreateParameter();
DbParameter phone = cmd.CreateParameter();

id.DbType = System.Data.DbType.Guid;
id.ParameterName = "@CustomerID";

address.DbType = System.Data.DbType.String;
address.ParameterName = "@Address";
address.Value = customer.Address;

name.DbType = System.Data.DbType.String;
name.ParameterName = "@Name";
name.Value = customer.Name;

phone.DbType = System.Data.DbType.String;
phone.ParameterName = "@Phone";
phone.Value = customer.Phone;

changeStamp.DbType = System.Data.DbType.DateTime;
changeStamp.ParameterName = "@ChangeStamp";
changeStamp.Value = DateTime.Now;

cmd.Parameters.Add(id);
cmd.Parameters.Add(address);
cmd.Parameters.Add(name);
cmd.Parameters.Add(phone);
cmd.Parameters.Add(changeStamp);

int i = 0;
try
{
if (customer.Id == Guid.Empty)
{
//Then we are adding a new
//customer to the database.
Guid guid = Guid.NewGuid();
id.Value = guid;
customer.Id = guid;
cmd.CommandText =
"insert into Customers(CustomerID, Address," +
" Name, ChangeStamp, Phone) " +
"Values(@CustomerID, @Address, @Name, " +
"@ChangeStamp, @Phone)";
}
else
{
//Then we are updating a database.
id.Value = customer.Id;
cmd.CommandText =
"update Customers set Address = " +
"@Address, Name = @Name, " +
"ChangeStamp = @ChangeStamp, "+
"Phone = @Phone where " +
"CustomerID = @CustomerID";
}
i = database.ExecuteNonQuery(cmd, null);
}
catch (SqlCeException ex)
{
throw ex;
}
catch (Exception ex)
{
throw ex;
}
finally
{
cmd.Dispose();
}

//Lets check if we saved the customer ok by
//evaluating the effected rows.
if (i > 0)
return true;
else
return false;
}

public bool Save(CustomerCollection customerCollection)
{
if (customerCollection == null)
throw new ArgumentException
(Properties.Resources.CustCollMustNotBeNull);

cmd =
database.DbProviderFactory.CreateCommand();
DbParameter id = cmd.CreateParameter();
DbParameter address = cmd.CreateParameter();
DbParameter name = cmd.CreateParameter();
DbParameter changeStamp = cmd.CreateParameter();
DbParameter phone = cmd.CreateParameter();

id.DbType = System.Data.DbType.Guid;
id.ParameterName = "@CustomerID";

address.DbType = System.Data.DbType.String;
address.ParameterName = "@Address";

name.DbType = System.Data.DbType.String;
name.ParameterName = "@Name";

phone.DbType = System.Data.DbType.String;
phone.ParameterName = "@Phone";

changeStamp.DbType = System.Data.DbType.DateTime;
changeStamp.ParameterName = "@ChangeStamp";

cmd.Parameters.Add(id);
cmd.Parameters.Add(address);
cmd.Parameters.Add(name);
cmd.Parameters.Add(phone);
cmd.Parameters.Add(changeStamp);
transaction = database.GetConnection().BeginTransaction();
cmd.Transaction = transaction;

foreach (CustomerInfo customer in customerCollection)
{
id.Value = customer.Id;
address.Value = customer.Address;
name.Value = customer.Name;
changeStamp.Value = DateTime.Now;
phone.Value = customer.Phone;

int i = 0;
try
{
if (customer.Id == Guid.Empty)
{
//Then we are adding a new
//customer to the database.
Guid guid = Guid.NewGuid();
id.Value = guid;
customer.Id = guid;
cmd.CommandText =
"insert into Customers(CustomerID, Address," +
" Name, ChangeStamp, Phone) " +
"Values(@CustomerID, @Address, @Name, " +
"@ChangeStamp, @Phone)";
}
else
{
//Then we are updating a database.
id.Value = customer.Id;
cmd.CommandText =
"update Customers set Address = " +
"@Address, Name = @Name, " +
"ChangeStamp = @ChangeStamp, " +
"Phone = @Phone where " +
"CustomerID = @CustomerID";
}
i = database.ExecuteNonQuery(cmd, null);

if (i <= 0) break; } catch (SqlCeException ex) { transaction.Rollback(); throw ex; } catch (Exception ex) { transaction.Rollback(); throw ex; } } } if (i > 0)
{
transaction.Commit();
return true;
}
else
{
transaction.Rollback();
return false;
}
}



#endregion

#region Private helper methods
private CustomerInfo GetCustomer(DbDataReader reader)
{
CustomerInfo customer = new CustomerInfo();
customer.Id = (Guid)reader["CustomerID"];
if (reader["Name"] != DBNull.Value)
customer.Name = (string)reader["Name"];
if (reader["ChangeStamp"] != DBNull.Value)
customer.ChangeStamp =
(DateTime)reader["ChangeStamp"];
if (reader["Address"] != DBNull.Value)
customer.Address = (int)reader["Address"];
if (reader["Phone"] != DBNull.Value)
customer.Phone = (string)reader["Phone"];
return customer;
}
#endregion

#region IDisable members
public void Dispose()
{
if (transaction != null)
{
transaction.Dispose();
transaction = null;
}

if (cmd != null)
{
cmd.Dispose();
cmd = null;
}

if (database != null)
{
database.Dispose();
database = null;
}
}
#endregion
}

public class CustomerInfo
{
#region Fields
private Guid _id = Guid.Empty;
private string _name = string.Empty;
private string _address = string.Empty;
private string _phone = string.Empty;
private Nullable<DateTime> _changeStamp = null;
#endregion

#region Properties
public Guid Id
{
get
{
return _id;
}
set
{
_id = value;
}
}

public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}

public string Address
{
get
{
return _id;
}
set
{
_id = value;
}
}

public string Phone
{
get
{
return _phone;
}
set
{
_phone = value;
}
}

public Nullable<DateTime> ChangeStamp
{
get
{
return _changeStamp;
}
set
{
_changeStamp = value;
}
}
}

public class CustomerCollection : CollectionBase
{
#region ctor
public CustomerCollection()
{
}
#endregion

#region Methods
public int Add(CustomerInfo customer)
{
return base.List.Add(customer);
}

public void Insert(int index, GatewayInfo gateway)
{
base.List.Insert(index, gateway);
}

public bool Contains(CustomerInfo Customer)
{
return base.List.Contains(customer);
}

public void Remove(CustomerInfo customer)
{
base.List.Remove(customer);
}
#endregion


#region Indexers
public CustomerInfo this[int index]
{
get
{
return (CustomerInfo)List[index];
}
set
{
List[index] = value;
}
}
#endregion
}

}


We can use the foreach keyword in the newly added Save method because the CustomerCollection inherits from CollectionBase which implements the IEnumerable interface for us so we don't have to.

The newly added Save method could of course be refactored to make use of the existing Save method as there is some repeated code there. My concentration was on providing an example on the transaction support.

Monday, June 25, 2007

Mobile Client Software Factory: Data Access Application Block - Part 1

There are very few simple examples on how to use the Data Access Application Block that comes with the Mobile Client Software Factory.

For those of you who do not know what the Mobile Client Software Factory is, it is a mobile version of the desktop Enterprise Library. You can download the Mobile Client Software Factory and read about it here. You can also see the patterns and practices Developer Centre for more information.

I will show a very simple example on reading and writing to a SQL Server Mobile database using the Mobile Client Software Factory Data Access Application Block.

I have removed XML comments from the sample code to make it easier to read in an HTML page.

1. Firstly you will need to add a reference to your project to include the Microsoft.Practices.Mobile.DataAccess.dll.

This assembly can be found in the default directory:
C:\Program Files\Microsoft Mobile Client Software Factory\ApplicationBlocks\DataAccess\Src\bin\release.

Right click the References folder for your project in Visual Studio and click Add Reference.



2. You need to add the declarations to the header of your class.



using System;
using System.Collections.Generic;
using Microsoft.Practices.Mobile.DataAccess;
using System.Data.SqlServerCe;
using System.Data.Common;




namespace MyCompany.DAL
{
public class CustomerDB
{
}
}


3. Create an instance of the SqlDatabase class which is part of the Microsoft.Practices.Mobile.DataAccess namespace. A
good place to put this is in a constructor for your data access layer class. ie:-

using System;
using System.Collections.Generic;
using Microsoft.Practices.Mobile.DataAccess;
using System.Data.SqlServerCe;
using System.Data.Common;

namespace MyCompany.DAL
{
public class CustomerDB
{
#region Fields
private SqlDatabase database = null;
#endregion

#region ctor
public CustomersDB()
{
database = new SqlDatabase("Data Source = \StorageCard\" +
"MyDatabase.sdf; password=mypassword;encrypt " +
"database = TRUE");
}
#endregion

}
}

4. Implement the IDisposable interface so that when the class is disposed the database is closed. This enables the client to use the using statement.

using System;
using System.Collections.Generic;
using Microsoft.Practices.Mobile.DataAccess;
using System.Data.SqlServerCe;
using System.Data.Common;

namespace MyCompany.DAL
{
public class CustomersDB : IDisposable
{
#region Fields
private SqlDatabase database = null;
#endregion

#region ctor
public CustomersDB()
{
database = new SqlDatabase("Data Source = \Storage Card\" +
"MyDatabase.sdf; password=mypassword;encrypt " +
"database = TRUE");
}
#endregion

#region IDisable members
public void Dispose()
{
if (database != null)
{
database.Dispose();
database = null;
}
}
#endregion
}
}


5. Implement a GetCustomer method which demonstrates reading from the database.

Also note, stored procedures are not supported in SQL Server Mobile, so the next best thing is parameterized queries as demonstrated below. Also note from the code, we use a simple object model to pass back the data and we use the DataReader class to do this for performance reasons. I firmly believe in object models rather than DataSets. I have included the CustomerInfo class within the same namespace as the data layer, in the real world you wouldn't do this, it's there for simplicity.

Read up on ADO.NET Performance for more information on using DataReaders and why they are best practice.

using System;
using System.Collections.Generic;
using Microsoft.Practices.Mobile.DataAccess;
using System.Data.SqlServerCe;
using System.Data.Common;

namespace MyCompany.DAL
{
public class CustomersDB : IDisposable
{
#region Fields
private SqlDatabase database = null;
#endregion

#region ctor
public CustomersDB()
{
database = new SqlDatabase("Data Source = \Storage Card\"+
"MyDatabase.sdf; password=mypassword;encrypt "+
"database = TRUE");
}
#endregion

#region Methods
public CustomerInfo GetCustomer(Guid guid)
{
CustomerInfo customer = null;
if (guid = Guid.Empty)
return customer;

DbCommand cmd =
database.DbProviderFactory.CreateCommand();

DbParameter pname = cmd.CreateParameter();
id.DbType = System.Data.DbType.Guid;
id.ParameterName = "@CustomerID";
id.Value = guid;

cmd.Parameters.Add(id);

//Now for the SQL.
cmd.CommandText = "select * from Customers where "+
"CustomerID = @CustomerID";
DbDataReader reader = null;

try
{
reader = database.ExecuteReader(cmd, null);
if (reader.Read())
customer = GetCustomer(reader);
}
catch (SqlCeException ex)
{
throw ex;
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (reader != null)
reader.Close();
cmd.Dispose();
}
return customer;
}
#endregion

#region Private helper methods
private CustomerInfo GetCustomer(DbDataReader reader)
{
CustomerInfo customer = new CustomerInfo();
customer.Id = (Guid)reader["CustomerID"];
if (reader["Name"] != DBNull.Value)
customer.Name = (string)reader["Name"];
if (reader["ChangeStamp"] != DBNull.Value)
customer.ChangeStamp =
(DateTime)reader["ChangeStamp"];
if (reader["Address"] != DBNull.Value)
customer.Address = (int)reader["Address"];
if (reader["Phone"] != DBNull.Value)
customer.Phone = (string)reader["Phone"];
return customer;
}
#endregion

#region IDisable members
public void Dispose()
{
if (database != null)
{
database.Dispose();
database = null;
}
}
#endregion
}

public class CustomerInfo
{
#region Fields
private Guid _id = Guid.Empty;
private string _name = string.Empty;
private string _address = string.Empty;
private string _phone = string.Empty;
private Nullable<DateTime> _changeStamp = null;
#endregion

#region Properties
public Guid Id
{
get
{
return _id;
}
set
{
_id = value;
}
}

public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}

public string Address
{
get
{
return _id;
}
set
{
_id = value;
}
}

public string Phone
{
get
{
return _phone;
}
set
{
_phone = value;
}
}

public Nullable<DateTime> ChangeStamp
{
get
{
return _changeStamp;
}
set
{
_changeStamp = value;
}
}
}
}

6. Now the last part is implementing a write example and updating an existing customer record. So below I have added the Save method to the CustomersDB class. Its job is to update the customer record or create a new one.

using System;
using System.Collections.Generic;
using Microsoft.Practices.Mobile.DataAccess;
using System.Data.SqlServerCe;
using System.Data.Common;

namespace MyCompany.DAL
{
public class CustomersDB : IDisposable
{
#region Fields
private SqlDatabase database = null;
#endregion

#region ctor
public CustomersDB()
{
database = new SqlDatabase("Data Source = \Storage Card" +
"\MyDatabase.sdf; password=mypassword;encrypt "+
"database = TRUE");
}
#endregion

#region Methods
public CustomerInfo GetCustomer(Guid guid)
{
CustomerInfo customer = null;
if (guid = Guid.Empty)
return customer;

DbCommand cmd =
database.DbProviderFactory.CreateCommand();
DbParameter pname = cmd.CreateParameter();
id.DbType = System.Data.DbType.Guid;
id.ParameterName = "@CustomerID";
id.Value = guid;

cmd.Parameters.Add(id);

//Now for the SQL.
cmd.CommandText =
"select * from Customers where CustomerID = @CustomerID";
DbDataReader reader = null;

try
{
reader = database.ExecuteReader(cmd, null);
if (reader.Read())
customer = GetCustomer(reader);
}
catch (SqlCeException ex)
{
throw ex;
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (reader != null)
reader.Close();
cmd.Dispose();
}
return customer;
}

public bool Save(ref CustomerInfo customer)
{
if (customer == null)
throw new ArgumentException
(Properties.Resources.CustMustNotBeNull);

DbCommand cmd =
database.DbProviderFactory.CreateCommand();
DbParameter id = cmd.CreateParameter();
DbParameter address = cmd.CreateParameter();
DbParameter name = cmd.CreateParameter();
DbParameter changeStamp = cmd.CreateParameter();
DbParameter phone = cmd.CreateParameter();

id.DbType = System.Data.DbType.Guid;
id.ParameterName = "@CustomerID";

address.DbType = System.Data.DbType.String;
address.ParameterName = "@Address";
address.Value = customer.Address;

name.DbType = System.Data.DbType.String;
name.ParameterName = "@Name";
name.Value = customer.Name;

phone.DbType = System.Data.DbType.String;
phone.ParameterName = "@Phone";
phone.Value = customer.Phone;

changeStamp.DbType = System.Data.DbType.DateTime;
changeStamp.ParameterName = "@ChangeStamp";
changeStamp.Value = DateTime.Now;

cmd.Parameters.Add(id);
cmd.Parameters.Add(address);
cmd.Parameters.Add(name);
cmd.Parameters.Add(phone);
cmd.Parameters.Add(changeStamp);

int i = 0;
try
{
if (customer.Id == Guid.Empty)
{
//Then we are adding a new
//customer to the database.
Guid guid = Guid.NewGuid();
id.Value = guid;
customer.Id = guid;
cmd.CommandText =
"insert into Customers(CustomerID, Address," +
" Name, ChangeStamp, Phone) " +
"Values(@CustomerID, @Address, @Name, " +
"@ChangeStamp, @Phone)";
}
else
{
//Then we are updating a database.
id.Value = customer.Id;
cmd.CommandText =
"update Customers set Address = " +
"@Address, Name = @Name, " +
"ChangeStamp = @ChangeStamp, "+
"Phone = @Phone where " +
"CustomerID = @CustomerID";
}
i = database.ExecuteNonQuery(cmd, null);
}
catch (SqlCeException ex)
{
throw ex;
}
catch (Exception ex)
{
throw ex;
}
finally
{
cmd.Dispose();
}

//Lets check if we saved the customer ok by
//evaluating the effected rows.
if (i > 0)
return true;
else
return false;
}

#endregion

#region Private helper methods
private CustomerInfo GetCustomer(DbDataReader reader)
{
CustomerInfo customer = new CustomerInfo();
customer.Id = (Guid)reader["CustomerID"];
if (reader["Name"] != DBNull.Value)
customer.Name = (string)reader["Name"];
if (reader["ChangeStamp"] != DBNull.Value)
customer.ChangeStamp =
(DateTime)reader["ChangeStamp"];
if (reader["Address"] != DBNull.Value)
customer.Address = (int)reader["Address"];
if (reader["Phone"] != DBNull.Value)
customer.Phone = (string)reader["Phone"];
return customer;
}
#endregion

#region IDisable members
public void Dispose()
{
if (database != null)
{
database.Dispose();
database = null;
}
}
#endregion
}

public class CustomerInfo
{
#region Fields
private Guid _id = Guid.Empty;
private string _name = string.Empty;
private string _address = string.Empty;
private string _phone = string.Empty;
private Nullable<DateTime> _changeStamp = null;
#endregion

#region Properties
public Guid Id
{
get
{
return _id;
}
set
{
_id = value;
}
}

public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}

public string Address
{
get
{
return _id;
}
set
{
_id = value;
}
}

public string Phone
{
get
{
return _phone;
}
set
{
_phone = value;
}
}

public Nullable<DateTime> ChangeStamp
{
get
{
return _changeStamp;
}
set
{
_changeStamp = value;
}
}
}
}

7. Calling our CustomersDB class. You would typically call this class from your business layer. If we wanted to add a new customer to our Customer table, something like the following would work quite nicely:-

using (CustomersDB customersDb = new CustomersDB())
{
CustomerInfo myCustomer = new CustomerInfo();
myCustomer.Name = "Simon Hart";
myCustomer.Address = "London, England";
myCustomer.Phone = "02012736213";
customersDb.Save(myCustomer);
}

Executing the following code would create the customer and automatically close the database as we implemented the IDisposable interface.

Note the very simple object model. Of course this type of model would be much more complex in the real world but I have kept it simple here for demo purposes.

As you can see, using the Data Access Application block makes the code very simple and clean. We need not worry about messy ADO.NET statements.

The only cumbersome part of the code is declaring the parameters - no doubt this could be improved - I'd be interested in any comments.

I believe LINQ will make this even easier when version 3.5 of the Compact Framework is released.

This article is a first part to my talk on the Data Access Application Block. I will be writing some more which will include writing Transactions and collections. The above code is fine when writing single objects, but how do we write multiple records and wrap these in a Trasaction object to enable us to rollback if a particluar object failed for whatever reason? watch this space!

If you like my blog you can subscribe using a news aggregator, see here for more information.

UPDATE: See part 2 to this series here.

Sunday, June 24, 2007

iPhone Rival

It seems HTC has released a phone that rivals the Apple iPhone. Except this phone runs Windows Mobile 6 and can be used as a business tool.

See here for information on the HTC Touch.

You can bet this will be the first of many devices of its kind.

HTC call it TouchFLOW and it looks great!

VS "Orcas" and CF 3.5 beta 1

Although it has been available for download for sometime and posted on other sites/forums, I thought I'd post the link here.

The download is a Microsoft Virtual PC image, so you'll need Microsoft Virtual PC 2004 + SP1 to use it.

Some of the new interesting features are as follows:

1. System.IO.Compression support (this has got to make things easier)
2. LINQ subset
3. WCF subset

Daniel Moth talked about LINQ at the recent MSDN roadshow in London. The talk wasn't specific to devices but for the desktop but the same syntax and functionality is supported in the coming releases of the CF. All I can say is, watch out for LINQ!

LINQ will be available in the .NET Framework 3.5 and onwards. It is actually a language enhancement, so will be available in C# 3.0 and VB 9.0. But just to be clear, in order to use LINQ, you need version 3.5 of the .NET Framework - which is based on v2.0 of the CLR.

Once I have had a play with the WCF features with regards to the CF, I will blog about my experiences here.

Saturday, June 23, 2007

RSS, Atom - News Aggregators

I have recently downloaded and installed two RSS readers:

SharpReader
Rss Bandit

They are both very good products and both free although if you like either product, you can make a donation.

If you would like to subscribe to my blog, you can simply paste the following link into either aggregator: http://simonrhart.blogspot.com/feeds/posts/default it's an Atom feed but both products support RSS and Atom.

Personally, I much prefer Rss Bandit I think it's a great product and worthy of a donation.

Microsoft Feature Pack Upgrade

If you have a Windows Mobile 5 device that was shipped before MSFP (Microsoft Feature Pack) was released then you'll probebly find your OEM has released a ROM update for your device.

If you want to make use of Push Email like the Blackberry range of devices, then you will want to install this update.

Most major providers were quick to release ROM updates. I recently updated an Orange M3100 with the MSFP ROM update and it worked just fine. I only have another 1199 devices to do! I think I will delegate!

If you too have an Orange M3100 you can get the update from here: http://www.orange.co.uk/business/downloads/SPV_M3100_ROMUpdate_0407.exe
Release notes for the M3100 can be found here: http://www.business.orange.co.uk/servlet/Satellite?pagename=Business&c=OUKPage&cid=1044134915191

Microsoft left it to the OEM's to provide the update, so you will need to check with your provider if they have one.

Note: One major requirement for push email to work via WM5/6 is Microsoft Exchange Server 2003 SP2 or later is required.

Friday, June 22, 2007

.NET Compact Framework FAQ

As I get emailed and see so many repeated questions within various forums, I thought I'd post the MSDN .NET Compact Framework FAQ link here.

If your question still goes unanswered, then you might want to try and post your question to the Compact Framework user group here or the Pocket PC Developer user group here might help answer your question.

If you have a device networking problem, then the following group might be able to help with your problem.

I actively participate in all the above forums most days.

Visual Inheritance is Currently Disabled

Although CF2 has been released now since 07 November 2005, I'd thought I'd blog about this "issue" I received some time ago. As you are probebly aware, CF2 supports the UserControl class so design time support is much easier than it used to be in CF1. However, you might still get an error message after adding your new custom control to a form: "Visual Inheritance is Currently Disabled".

You might typically get this when the control uses P/Invokes or device level funtionality not supported on the desktop - such as the WindowsCE assembly.

There are two ways to get around this problem:-

1. Add an .Xmta (Design Time Attributes) file to your project.
2. Add the DesktopCompatible(true) attribute to the user control.

The prefered method is to add an .xmta (Design Time Attributes) file to your assembly. This is because typically if you have a library of controls you won't need to keep using the DesktopCompatible attribute for every form that uses your control. In addition you can add comments for memebers that will show up in the designer when using an .xmta file. The xml schema should be as follows:

<?xml version="1.0" encoding="utf-16"?>
<Classes xmlns="http://schemas.microsoft.com/VisualStudio/2004/03/SmartDevices/XMTA.xsd">
<Class Name="Company.Mobile.Forms.PropertyHeader">
<DesktopCompatible>true</DesktopCompatible>
<Property Name="Description">
<Description>Displays the description of the parent forms function.</Description>
</Property>
</Class>
</Classes>

The above example would be used in a case where your user control's class was named: Company.Mobile.Forms.PropertyHeader.

Tuesday, June 19, 2007

Provisioning XML with CSP's

A couple of my other posts mentioned using Configuration Service Providers but I didn't mention how to actually provision the XML once created in the correct format.

Doing this is easy in CF2 and WM5 you can use the new assembly which comes with WM5 SDK Microsoft.WindowsMobile.Configuration.dll. ie:

using Microsoft.WindowsMobile.Configuration;

//xmlDoc is of type XmlDocument which contains a valid CSP schema.
ConfigurationManager.ProcessConfiguration(xmlDoc, false);

Of course this is not so easy on pre-WM5 as the *.WindowsMobile.* assembly range of API's only work on WM5 and later.

You can P/Invoke DMProcessConfigXML or you could use InTheHand.WindowsMobile.Configuration. in the hand have written a managed wrapper that will provision your XML on pre Windows Mobile 5 devices and I believe it's free. ie:

using InTheHand.WindowsMobile.Configuration;

//xmlDoc is of type XmlDocument which contains a valid CSP schema.
ConfigurationManager.ProcessConfiguration(xmlDoc, false);

Microsoft Virtual Labs

I was talking to a colleque the other day about Microsoft Virtual Labs and suprisingly, he had never heard of it. So I thought I'd post a link here.

http://msdn2.microsoft.com/en-us/virtuallabs/default.aspx

What is it?
Microsoft Virtual Labs is a way to learn new content without having to install the software on your machine. It includes a guided hands-on walk through which usually lasts around 1 hour. It works a little like Terminal Services but via a web browser.

Tuesday, June 12, 2007

Cracking .NET code

After responding to a post recently regarding strong naming .NET assemblies I replied with the fact that this code can be broken. The mis-conception that strong naming your code will protect it is not true.

Digitally signing your code and writing algorithms in unmanaged code will protect it as much as possible.

See here for removing a strong name: http://www.atrevido.net/blog/PermaLink.aspx?guid=f772c18a-f389-4c28-bd6a-a30f4ccc84f5

See here for cracking an obfucated peice of code: http://www.atrevido.net/blog/PermaLink.aspx?guid=8315fa01-0286-47ce-a20b-fcc15eb297c3

See here for more information of strong naming: http://msdn2.microsoft.com/en-us/library/wd40t7ad(VS.80).aspx (.NET 2.0)

A simple solution is to make your algorithm's more complex and harder to break and to write them in unmanaged C++ then P/Invoke your unmanaged routine from your unmanaged routine.

Preventing the device from turning off

One question a lot of people ask is how to prevent the device from turning itself off?

You can either call the SHIdleTimerReset function in aygshell.dll:

[DllImport("aygshell.dll")]
private static extern void SHIdleTimerReset();

See here for more information on the API: http://msdn2.microsoft.com/en-us/library/ms835757.aspx

Or use the already wrapped method defined in OpenNETCF.WindowsCE.PowerManagement.ResetSystemIdleTimer();

Of course the function needs to be called periodically before the timeout threshold. http://www.opennetcf.org/

Saturday, June 09, 2007

Locking down individual programs in Windows Mobile

There is no API for locking down certain elements (programs, functions) on Windows Mobile and in fact there is not a lot of information out there on how to do such a thing.

There is information about security policies on particular Windows Mobile 5 and 6 and testing tools such as the Device Security Manager. For information about security policies and certificates, see this blog. I am currently in the middle of writing a document about the signing and creating of certificates which I will publish soon…

So back to a simple way of locking down an individual program….

There are three ways for locking down Windows Mobile. They are:

● Kiosk solution (SPB Kiosk)
● Long hand (code “hack”)
● Lock-down product (Trust Digital)

Of course the Kiosk solution or any of the lock down products are the easiest, but if you are fussy about using third party software or trying to keep costs down, then the long hand option might be the way to go. In addition the Kisok solution requires full screen which in some cases is not desirable. Personally I like to stick to using my own code rather than using third party solutions. Based on this, I will talk about the Long hand (code) option.

A silly simple way to stop end users from running an application is to create a zero-byte filename that you wish to block. So for example if you didn’t want a handful of your users using Pocket Internet Explorer, then you would create a zero-byte file named iexplore.exe in the \Windows directory. I know this sounds strange that it might overwrite the existing file, but it merely “hides” it. In order to enable Pocket Internet Explorer again just simply delete the zero-byte file.

This is all fine, but what about the shortcuts? Simply tapping them after locking down the .exe will generate an error message that the application could not be found. A better way to handle this situation is to delete the shortcut file. For example if Pocket Internet Explorer is present in the Start Menu, you would need to delete the shortcut file: \Windows\Start Menu\Internet Explorer.lnk.

There is one thing you should bear in mind when deleting the shortcut file and that is when locking down File Explorer (\Windows\fexplore.exe) – among others the shortcut file is marked as read-only. A simple way of getting around this is to mark the Attribute property for the FileInfo object as Archive. IE:

FileInfo ieShortcutStartMenuFile = new FileInfo(@“\Windows\Start Menu\Internet Explorer.lnk”);
ieShortcutStartMenuFile.Attributes = FileAttributes.Archive;
ieShortcutStartMenuFile.Delete();

Of course when you need to re-enable the application you will not only need to delete the zero-byte file but create the shortcut file as well.

This method of locking down applications can be applied throughout the device for all programs if required including some of the ROM installed apps:

Phone (for Phone Edition devices cprog)
Word Mobile
Word Excel
File Explorer
Camera

It is also possible to limit access to the Settings window. This is as easy as deleting the \Windows\Start Menu\Settings folder. There is nothing contained in this folder, so you won’t lose any shortcuts/data. To re-enable, simply re-create the folder.

Friday, June 01, 2007

Unloading an application Programmatically

In Pocket PC 2003 and earlier you could use unload.exe to unload applications that had been marked with flag NoUninstall as true.

This is not quite possible with later devices - Windows Mobile 5 and onwards. Instead Microsoft encourages the practice of using the Uninstall Configuration Service Provider (this link is for WM5 SDK but works on older devices). The cool thing about using the Uninstall CSP is that, you need not terminate the process if it is already loaded in memory before uninstalling it like you had to do when using unload.exe, instead you can just execute the CSP and it will handle shutting down the process for you. Of course it is recommended to ask the process to terminate rather than letting CSP do it but this shows just how powerful the uninstall CSP is.

Usage of the Uninstall CSP is dependent on the security roles that the OEM sets for individual devices. If unsure, ask your OEM if this CSP has been enabled. See here for a list of default roles for CSP's.

See here on how to provision your XML.

Monday, May 28, 2007

Smart Client Software Factory New Release

May 2007 version of the Smart Client Software Factory has just been released.

There are a few new blocks in this release, the interesting ones are:

  • Connection Manager Application Block
  • Disconnected Service Agent Application Block
You can download the Smart Client Software Factory from here: http://msdn2.microsoft.com/en-us/library/aa480482.aspx

UPDATE: If you want the Mobile Client Software Factory, get it here.

Smart Client Software Factory: Orientation Aware Control

The Orientation Aware Control which comes as part of the patterns and practices Mobile Client Software Factory for Windows Mobile, enables you to write one set of source to target a range of multiple form factors, resolutions and screen orientations.

The block (or shall I say factory), implements the "magic" similar to how localization is done in .NET.

You can download the latest (May 2007) Smart Client Software Factory here: http://msdn2.microsoft.com/en-us/library/aa480482.aspx

I also recently came across the following Web Cast which is very informative into the effectiveness and use of this particular block: http://msevents.microsoft.com/cui/WebCastEventDetails.aspx?culture=en-US&EventID=1032310458&CountryCode=US

Monday, May 07, 2007

Channel 9 Windows Mobile 6

Interesting video recently posted on Channel 9. Some really cool new features in particular voice recognition on WM6 - can't wait to get my hands on one of these!

http://channel9.msdn.com/Showpost.aspx?postid=303900

Saturday, May 05, 2007

Enterprise Library 3.0 Released

There are some new Application Blocks in this April 2007 Release as well as .NET 3 support. Some interesting ones are:

Validation Application Block - This is worth a look
Policy Injection Application Block

Get the source here: http://www.microsoft.com/downloads/details.aspx?familyid=62ef5f79-daf2-43af-9897-d926f03b9e60&displaylang=en

Sunday, March 25, 2007

Windows Mobile 6 SDK Released

The new Windows Mobile 6 SDK's have now been released, codenamed "Crossbow".

The names have changed again though!

The Smartphone is now named: Windows Mobile Standard

The Pocket PC / Windows Mobile is now named: Windows Mobile Classic

The Pocket PC / Windows Mobile Phone Edition is now named: Windows Mobile Professional

Download:
http://www.microsoft.com/downloads/details.aspx?familyid=06111a3a-a651-4745-88ef-3d48091a390b&displaylang=en

Implementing Tap and Hold funtionality in CF

When creating your own user control maybe an owner drawn list of items in either CF1 or CF2 you might want to implement the standard Windows Mobile tap and hold little red dots that appear can be achieved by P/invoking SHRecognizeGesture which can be found in the aygshell.dll module.

The following code shows how this can be achieved. You would typically call the ShowMenu() method from an OnMouseDown event in your user control.



using System;

namespace RecognizeGesture
{

public class RGesture
{
///
/// Structure used by SHRecognizeGesture
///

internal struct SHRGINFO
{
public int cbSize;
public IntPtr hwndClient;
public int ptDownX;
public int ptDownY;
public SHRGFLags dwFlags;
}

///
/// SHRGINFO flags
///

[Flags]
internal enum SHRGFLags
{
SHRG_RETURNCMD = 0x00000001,
SHRG_NOTIFYPARENT = 0x00000002,
///
/// use the longer (mixed ink) delay timer
/// useful for cases where you might click down first, verify you're
/// got the right spot, then start dragging... and it's not clear
/// you wanted a context menu
///

SHRG_LONGDELAY = 0x00000008,
SHRG_NOANIMATION = 0x00000010,
}

[DllImport("aygshell")]
extern private static int SHRecognizeGesture(ref SHRGINFO shr);

private void ShowMenu()
{
SHRGINFO shr = new SHRGINFO();
shr.cbSize = Marshal.SizeOf(typeof(SHRGINFO));
shr.dwFlags = SHRGFLags.SHRG_RETURNCMD;
shr.ptDownX = x;
shr.ptDownY = y;
shr.hwndClient = Handle;

//Ask shell to track the gesture.
int ret = SHRecognizeGesture(ref shr);

//If user used tap-and-hold - track popup menu.
if (ret == 1000)
{
//Display menu.
}
}
}
}

Monday, March 19, 2007

Obfuscating CF Code

Many people have complained time and time again about the .NET Obfuscator that comes with Visual Studio (2003 and 2005) not working effectively with CF applications. I too have had my applications running and working until I obfuscate it using the tool that comes with VS and then my app doesn't behave correctly...

Well.... believe it or not but there are loads of .NET obfuscator tools available out there, I admit not free, but you get what you pay for in life ;)

Once you have obfuscated your code, you can use a free tool such as Reflector to check the results.

See this link: http://www.howtoselectguides.com/dotnet/obfuscators/

Executing a CAB file for Windows Mobile Devices

After recently wanting to do this I couldn't find much information about it on the web with regards to command-line parameters etc.

WCELOAD.exe is the application required to install CAB files for Windows CE. It is located in the \Windows directory.

/noui - Specifies no user interface while the install is taking place.

/noaskdesk - Do not ask the user where to install the application (Storage card, RAM).

/nouninstall - Specifies that the application cannot be removed. No .unload file will be created if this flag is set.

An example of the command could be:
\windows\wceload /noui /noaskdesk \Storage card\myapp.cab.

Note: If the /nouninstall flag is not set, an .unload file is created in the \Windows directory which has this naming convention: .unload.


This is a useful link that contains more information on the subject:
http://msdn2.microsoft.com/en-us/library/ms933760.aspx