Sunday, August 24, 2008

Tech-Ed Developers EMEA 2008

As I mentioned a few weeks ago Tech-Ed Developers EMEA 2008 is open for registration. I thought I'd mention that I'll be doing an interactive session on Windows Mobile push technology using all the latest cool developments in the mobile space.

I was supposed to do Tech-Ed in Orlando just gone but this didn't happen due to a confusion in the type of track I was going to do.

But I'm really looking forward to this event as Tech-Ed is Microsoft's biggest event of the year and should be really interesting to see what new developments are coming to market.

I know a few people from Edenbrook are going to attend Tech-Ed so it should be fun.

You can register here.

Saturday, August 23, 2008

Mobile Client Software Factory on Compact Framework 3.5

The Mobile Client Software Factory has been archived by Microsoft sadly. The last build was in July 2006 and the target environment was Windows Mobile 5 and VS 2005. This factory was posted on codeplex a while back but it seems to have been removed recently for whatever reason.

If you want to run any of the blocks against the Compact Framework 3.5 you simply need convert the solution to Visual Studio 2008 using the VS 2008 convertion wizard then target the CF 3.5. I have done this and it seems to work just fine. I've written an article here on how to do this - fairly simple stuff.

Working for Edenbrook

As I mentioned recently, I have changed jobs. I now work for a highly successful consultancy based in London called Edenbrook. I have only been working there for 2 weeks so far, but it's been a very good experience already. Edenbrook only emmployee the best candidates so the recruitment process is quite hard but the work and people you get to work with are of high calibre and fun and challenging.

From what I can tell Edenbrook value there employees greatly, so the social events etc are quite extensive. I have already been on a jolly which occupied the whole day including gokarting and ending in a pub near liverpool St. and not getting home until gone midnight.
Although in the morning we had some technical tracks where a few of our guys talked about some new technologies we are using to get everyone up to speed.

Looking through other peoples code/architecture and patterns as well as the design documentation etc ozzes quality. My first project started on Monday and is over in Dublin, Ireland. I'll probably be out there for a few months 3 days per week although the bit I'm working on shouldn't take too long no doubt I'll be doing something else over there when that is finished. It is quite fun and a change from working out of the same office for years on end - some projects never end! Although the 06:40 flight from London Monday mornings is tough!

From what I hear Edenbrook now has great mobility capabilities, I'm hoping some mobile projects come our way soon :)

Using Stored Procs on SQL CE and Windows Mobile

A cool pattern as described in the Windows Mobile Line of Business Solution Accelerator 2008 show how to implement Stored Procedures in SQL CE on Windows Mobile with the Compact Framework.

OK they are not really Stored Procedures as known in the full version of SQL Server as this wouldn't make sense on devices as SQL CE is embedded and runs inproc in the callers context. But this pattern shows a simple example of extracting that ugly SQL code from our lovely OO C#/VB code.

NOTE: We *have* to use SQL and hand cranked data models etc on devices due to the lack of support for ORM's on devices.

Take the following code (using Mobile Client Software Factory Data Access Block) which embeds SQL code in C#:
//Bad code
DbCommand cmd = database.DbProviderFactory.CreateCommand();
DbParameter id = cmd.CreateParameter();
id.DbType = System.Data.DbType.Int32;
id.ParameterName = "@CustomerId";
id.Value = guid;

cmd.Parameters.Add(id);

int i = 0;
try
{
//Now for the SQL.
cmd.CommandText = "delete from Customer where " +
"customerId = @CustomerId";
i = database.ExecuteNonQuery(cmd, null);
}
catch (SqlCeException)
{
throw;
}
finally
{
cmd.Dispose();
}
The above is quite ugly you probably agree or should agree :) Imagine though that there are loads of methods doing all sorts of complex stuff. It would be a nightmare to maintain that SQL. In addition how would you run that SQL on the desktop if you wanted to script stuff. Imagine also that you might have a method that creates the database from scratch, now that would be ugly in code.

The solution is to abstract the SQL out of the code into resource files containing *.sql scripts that your code calls. The added bonus with this pattern is it allows you to localize the SQL too if you wanted.

Take the good code:
//Good code
DbCommand cmd = connection.DbProviderFactory.CreateCommand();
DbParameter id = cmd.CreateParameter();
id.DbType = System.Data.DbType.Int32;
id.ParameterName = "@CustomerId";
id.Value = customerId;

cmd.Parameters.Add(id);

try
{
//Now for the SQL.
cmd.CommandText = Properties.StoredProcs.Customer_Del_ByCustomerId;
connection.ExecuteNonQuery(cmd, null);
}
catch (SqlCeException)
{
throw;
}
finally
{
cmd.Dispose();
}
Now the SQL looks like the following:
DELETE FROM Customer
WHERE CustomerId = @CustomerId
If you wanted to localize it, we simply add a satalite resource with another locale, easy. Of course depending on what you are doing the scripts will port over to the desktop.

Northwind sample database for SQL Server 2005 Express

I installed SQL Server 2005 Express edition on my new laptop the other day (part of Visual Studio 2008). And I realised that it doesn't include the widely known Northwind sample database that comes with other editions of SQL Server.

Anyway you can download the script to create this database here. The installer is for SQL Server 2000 but works on 2005 as well.

Friday, August 08, 2008

MSDN Roadshow Re-Run - London 24th Oct

If like myself you missed the last one in April this year, then fear not as there will be a re-run on Oct 24th in London.

Register here.

I have been told by Microsoft that this will not be as large as the last one.

Turn your Windows Mobile into an iPhone

Over at LifeHacker shows you how...
http://lifehacker.com/software/hack-attack/turn-your-windows-mobile-phone-into-an-iphone-269055.php

Loading to a web page using the default browser programmatically on Windows Mobile

Another common requirement is to show a specific URL on Windows Mobile using the default PIE. Not the UI control.

The code is very simple if running CF 2.0 or later, simply write something like the following:
System.Diagnostics.Process.Start("http://www.google.com", "");
The above works because Process wraps ShellExecute. In fact that code also works on the desktop too.

Thursday, August 07, 2008

When to use InvalidOperationException

I was out with friends the other day in a bar and somehow we started talking about when to throw InvalidOperationException from within your application code (exciting I know! :). I thought I'd clear this up as it can be a slightly gray area.

The description on MSDN is as follows: "The exception that is thrown when a method call is invalid for the object's current state".

So it should only ever be thrown if state within the class is not correct for the executing method.

A snip from Framework Design Guidelines book regarding InvalidOperationException is as follows: "Do throw an InvalidOperationException if the object is in an inappropriate state. The System.InvalidOperationException exception should be thrown if a property set or a method call is not appropriate given the objects current state. An example of this is writing to a FileStream that's been opened for reading.".

MSDN goes on comparing the difference between InvalidOperationException and ArgumentException and ArgumentNullException with: "If the method invocation failure is due to invalid arguments, then ArgumentException or one of its derived classes, ArgumentNullException or ArgumentOutOfRangeException, should be thrown instead.".


Creating a Network and a Connection Programmatically on Windows Mobile - Part 1

Many folks within the community ask questions about forcing a specific connection or using a proxy server in order to connect to the internet etc. Note this is only one way of achieving connectivity and in my opinion probably the best way.

I'd like to write an article on how to create a network and add a connection to a network using the CSP's Configuration Server Providers available to us under Windows CE.

First of all we use the CM_Networks CSP to create our custom network that we will use when connecting to our network. Which looks like this:



So it is fairly simple as compared to other CSPs. I have written a couple of articles about CSPs in the past here.

What is a network in the Windows Mobile space? a network is a bucket used to group certain connections, such as VPN, GPRS, 3G, proxy, GSM etc It is what your application should use when connecting to your remote network typically in LOB - Line of Business apps you'd use some form of APN - Access Point Name. You can view a list of connections on your device by navigating to Settings\Connections\Connections.


Connections screen

Navigating to the Advanced tab allows you to view or select different networks.


Advanced connections screen

Tapping select networks allows you to add, edit, delete or change connections.


Network management

Our objective is to add our own network and a private APN to this network. We can then select this network when connecting to our APN using the Connection Manager built into phone devices.

The first thing we need is the Microsoft.WindowsMobile.dll and the Microsoft.WindowsMobile.Configuration.dll both found from the WM5 or WM6 SDKs. Note: if you are targeting pre WM5 then you will need to use P/Invoke - I will talk about this later.

To create out network so that we can add connections, we need to call the CM_Networks CSP as mentioned earlier. The interface for CSPs is XML.

In the following code example we create a network named Acme Corp. I decided to use the XmlTextWriter class here but could have done this just by loading the XML into DOM using the XmlDocument class. The XML looks like the following:
<?xml version="1.0" encoding="utf-16"?>
<wap-provisioningdoc>
<characteristic type="CM_Networks">
<characteristic type="Acme Corp">
<parm name="DestId" value="{8b06c75c-d628-4b58-8fcd-43af276755fc}" />
</characteristic>
</characteristic>
</wap-provisioningdoc>
The code to produce this XML and to provision this XML is as follows:
StringBuilder sb = null;
StringWriter sw = null;
XmlTextWriter xmlWriter = null;
try
{
sb = new StringBuilder();
sw = new StringWriter(sb);
xmlWriter = new XmlTextWriter(sw);
xmlWriter.WriteStartDocument();
xmlWriter.WriteStartElement("wap-provisioningdoc");
xmlWriter.WriteStartElement("characteristic");
xmlWriter.WriteStartAttribute("type");
xmlWriter.WriteString("CM_Networks");
xmlWriter.WriteEndAttribute();
xmlWriter.WriteStartElement("characteristic");
xmlWriter.WriteStartAttribute("type");
xmlWriter.WriteString("Acme Corp");
xmlWriter.WriteEndAttribute(); //type
xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("DestId");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
xmlWriter.WriteString("{8b06c75c-d628-4b58-8fcd-43af276755fc}");
xmlWriter.WriteEndAttribute();
xmlWriter.WriteEndElement(); //parm
xmlWriter.WriteEndElement(); //characteristic
xmlWriter.WriteEndElement(); //characteristic
xmlWriter.WriteEndElement(); //wap-provisioningdoc
xmlWriter.WriteEndDocument();
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(sb.ToString());
ConfigurationManager.ProcessConfiguration(xmlDoc, false);
}
catch (Exception)
{
//Do stuff...
throw;
}
finally
{
if (xmlWriter != null)
{
xmlWriter.Flush();
xmlWriter.Close();
}
}

Now if we take a look in Connections\Advanced\Select Networks screen, we will see the newly created network. Although at the moment we have just a blank network with no connections defined.


Our newly created network

Tapping edit will reveal no connections as yet...


Empty network

Our next job is to add a connection to make this network useful. We do this using the CM_GPRSEntries CSP. Documentation on this CSP can be found here. 3G UMTS/HSDPA will be used if available although the CSP is named GPRS this is a little mis-leading.

Again we use the trusty old XmlTextWriter to create our XML file in order to provision to our device. The XML looks like the following:
<?xml version="1.0" encoding="utf-16"?>
<wap-provisioningdoc>
<characteristic type="CM_GPRSEntries">
<characteristic type="Acme Corp">
<parm name="DestId" value="{8b06c75c-d628-4b58-8fcd-43af276755fc}" />
<parm name="UserName" value="un" />
<parm name="Password" value="pa" />
<parm name="DnsAddr" value="192.168.0.1" />
<parm name="Domain" value="ACME" />
<parm name="AltDnsAddr" value="192.168.0.2" />
<parm name="WinsAddr" value="" />
<parm name="AltWinsAddr" value="" />
<parm name="SpecificNameServers" value="0" />
<parm name="IpHeaderCompression" value="false" />
<parm name="RequirePw" value="1" />
<parm name="RequireMsEncryptedPw" value="false" />
<parm name="RequireDataEncryption" value="false" />
<characteristic type="DevSpecificCellular">
<parm name="BearerInfoValid" value="1" />
<parm name="GPRSInfoValid" value="1" />
<parm name="GPRSInfoProtocolType" value="2" />
<parm name="GPRSInfoL2ProtocolType" value="PPP" />
<parm name="GPRSInfoAccessPointName" value="acmecorp.com" />
<parm name="GPRSInfoAddress" value="" />
<parm name="GPRSInfoDataCompression" value="2" />
<parm name="GPRSInfoHeaderCompression" value="2" />
</characteristic>
</characteristic>
</characteristic>
</wap-provisioningdoc>
The code to create this XML and provision the device looks like the following:
StringBuilder sb = null;
StringWriter sw = null;
XmlTextWriter xmlWriter = null;
try
{
sb = new StringBuilder();
sw = new StringWriter(sb);
xmlWriter = new XmlTextWriter(sw);
xmlWriter.WriteStartDocument();
xmlWriter.WriteStartElement("wap-provisioningdoc");
xmlWriter.WriteStartElement("characteristic");
xmlWriter.WriteStartAttribute("type");
xmlWriter.WriteString("CM_GPRSEntries");
xmlWriter.WriteEndAttribute(); //type
xmlWriter.WriteStartElement("characteristic");
xmlWriter.WriteStartAttribute("type");
//The name of the APN.
xmlWriter.WriteString("Acme Corp");
xmlWriter.WriteEndAttribute(); //type
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("DestId");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
xmlWriter.WriteString("{8b06c75c-d628-4b58-8fcd-43af276755fc}");
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("UserName");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
//Username to authenticate.
xmlWriter.WriteString("un");
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("Password");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
//Password to authenticate.
xmlWriter.WriteString("pa");
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("DnsAddr");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
//DNS of the APN.
xmlWriter.WriteString("192.168.0.1");
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("Domain");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
//Domain to use during authentication.
xmlWriter.WriteString("ACME");
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("AltDnsAddr");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
//An alternate DNS.
xmlWriter.WriteString("192.168.0.2");
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("WinsAddr");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
//The WINS server is ignored unless SpecificNameServers is set to true.
xmlWriter.WriteString("");
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("AltWinsAddr");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
xmlWriter.WriteString("");
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("SpecificNameServers");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
//1 = true, 0 = false
xmlWriter.WriteString("0");
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("IpHeaderCompression");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
xmlWriter.WriteString("false");
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("RequirePw");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
xmlWriter.WriteString("1"); //true, 0 = false
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  
xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("RequireMsEncryptedPw");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
xmlWriter.WriteString("false");
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("RequireDataEncryption");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
xmlWriter.WriteString("false");
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("characteristic");
xmlWriter.WriteStartAttribute("type");
xmlWriter.WriteString("DevSpecificCellular");
xmlWriter.WriteEndAttribute(); //type
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("BearerInfoValid");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
xmlWriter.WriteString("1");
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("GPRSInfoValid");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
xmlWriter.WriteString("1"); //true
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("GPRSInfoProtocolType");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
xmlWriter.WriteString("2"); //CELLDEVCONFIG_GPRSPROTOCOL_IP
//See: http://msdn2.microsoft.com/en-us/library/ms835762.aspx
//for the CELLGPRSCONNECTIONINFO structure.
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("GPRSInfoL2ProtocolType");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
xmlWriter.WriteString("PPP");
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("GPRSInfoAccessPointName");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
xmlWriter.WriteString("acmecorp.com");
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("GPRSInfoAddress");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
xmlWriter.WriteString("");
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("GPRSInfoDataCompression");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
//Data compression.
xmlWriter.WriteString("2"); //true, 1 = false
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm

xmlWriter.WriteStartElement("parm");
xmlWriter.WriteStartAttribute("name");
xmlWriter.WriteString("GPRSInfoHeaderCompression");
xmlWriter.WriteEndAttribute(); //name
xmlWriter.WriteStartAttribute("value");
xmlWriter.WriteString("2"); //true, 1 = false
xmlWriter.WriteEndAttribute(); //value
xmlWriter.WriteEndElement(); //parm
  
xmlWriter.WriteEndElement(); //characteristic
xmlWriter.WriteEndElement(); //characteristic
xmlWriter.WriteEndElement(); //characteristic
xmlWriter.WriteEndElement(); //wap-provisioningdoc
  
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(sb.ToString());
ConfigurationManager.ProcessConfiguration(xmlDoc, false);
}
catch (Exception)
{
throw;
}
finally
{
if (xmlWriter != null)
{
xmlWriter.Flush();
xmlWriter.Close();
}
}
After we run this code, if we go back to the network, we will see a new connection added to this network:


Our new connection added to our custom network.

A few points to make with the above code. The DestId must be the same Id provided when creating the network. The CM_GPRSEntries is a little more complex than the network CSP.

UserName - if blank and the RequirePw is set, then the Connection Manager will ask for credentials when making a connection, otherwise authentication will fail unless anonymous access has been defined.

DnsAddr - only needs to be set if required by the APN. Set it to 0.0.0.0 to null the address. This is only ever used if SpecificNameServers is set to true (1 = true 0 = false). This is true for WinsAddr, AltDnsAddr.

IpHeaderCompression - should be set for PPP connections as it significantly improves performance. The property should be set to false only when connecting to a server that does not correctly negotiate IP header compression.

RequirePw - as mentioned before, the system should prompt for a user/password/domain name before dialing the phone. If set to true, the system will prompt if no password or user name is provisioned. If set to false, the system will not prompt for password before dialing even if no password or user name is provisioned.

RequireMsEncryptedPw - USE WITH CAUTION: determines whether MS encrypted password is required or not. If set, only the Microsoft secure password scheme, MSCHAP, can be used to authenticate the client with the server. This property prevents the PPP driver from using the PPP plain-text authentication protocol (PAP).

RequireDateEncryption - USE WITH CAUTION: determines whether data encryption is required or not. Note: MsEncryptedPassword property needs to be set in order for this property to have any effect. Also, MsEncryptedPassword is only supported by the Microsoft secure password scheme, MSCHAP (based on MD5), can be used to authenticate the client with the server. The PPP device driver disables plain PAP authentication. For interopability, set this flag to false.

GPRSInfoL2ProtocolType - PPP is the only supported protocol on Win CE devices.

GPRSInfoDataCompression - determines whether data compression for messages that are sent and received is enabled or not.

GPRSInfoHeaderCompression - header compression at message level is enabled or not. (2 = true 1 = false).

GPRSInfoAccessPointName - this is the name/IP of the APN.

Notice I have used the ConfigurationManager.ProcessConfiguration method to provision the XML which is only available on CF 2.0 and later frameworks. You have to platform invoke DMProcessConfigXML on pre CF 2.0.

In the next part, I will show how to add a proxy server to this connection.

UPDATE: As I mentioned above, I was going to show in the next post how to add a proxy connection. I've done one better, I've created a managed Configuration Service Provider API. I've written an article that has recently been published on MSDN which you can view here: http://msdn.microsoft.com/en-us/library/dd296760.aspx .

The API source code with an example can be downloaded from the MSDN Code Gallery here: http://code.msdn.microsoft.com/Project/Download/FileDownload.aspx?ProjectName=CSPWM&DownloadId=3943.

I hope you find it useful. In a future post, I'll build on this managed CSP with a proxy managed CSP!

Wednesday, August 06, 2008

Joining Edenbrook

Edenbrook

I have recently decided to move on from my current (or shall I say previous) employer which was a hard decision to make. I have decided to join Edenbrook.

Edenbrook are a consultancy based in London that specialise in Microsoft and Oracle solutions. Edenbrook are accredited with the highest partnership status by Microsoft and Oracle, that is Microsoft Gold Partner and Oracle Certified Partner. They have been in the Sunday Times Tech Track 100 three times and once in the Profit Track 100 since 2004.

Edenbrook has a proven track record of working with many different sectors for FTSE 250 companies with extensive emphasis on integration with existing systems as well as development of new solutions.

Edenbrook now has a managed services arm which provides outsourcing from it's offices in Pune, India as well as support services.

Monday, August 04, 2008

Showing the phone programmatically on Windows Mobile

Another common feature people ask for is how to show the phone from your application. This can be done as follows:
System.Diagnostics.Process.Start(@"\Windows\cprog.exe", string.Empty);
The above will work on CF 2.0 and later. Of course this will only work on phone edition/professional devices.

Saturday, August 02, 2008

Showing a list of contacts programmatically on Windows Mobile

A question that gets asked quite frequently in the communities, is "how do I show a list of contacts contained within the PIM on Windows Mobile devices programmatically".

Well the answer is very simple if running CF 2.0 or later simply write something like the following:
System.Diagnostics.Process.Start("poutlook.exe", "contacts");
So we start process poutlook and pass contacts as a parameter. If you're using pre-CF 2.0 then you'll have to P/invoke ShellExecuteEx Win32 API.

Of course the code above will use the Outlook Mobile application to display contacts on Windows Mobile. To write you're own UI you'd have to use the POOM API to populate you're own UI.

Tuesday, July 29, 2008

LINQ - Level 100

I've been talking about LINQ recently and the more I learn, the more I write as I think it is a well cool technology that I like talking about :) But if you're starting out and not sure how to start using LINQ what do you do?

This post is level 100 so very high level and it is meant to give newcomers an overview on how to start with LINQ, ie what tools you need what version of Visual Studio, language versions supported platforms, what's supported on devices and finally some links to get started.

Visual Studio 2008

You will need to use the new language features only available in C# 3.0 and VB 9.0 which can be used with Visual Studio 2008. I have shown code examples in C# but the concepts are the same for VB.

So you'll need at least Visual Studio 2008 and target 3.5 of the framework.

Types of LINQ (all supported on the desktop)

  1. LINQ to SQL (old name DLINQ)
  2. LINQ to XML
  3. LINQ to DataSets
  4. LINQ to Entities (sometimes called LINQ to ADO.NET)
  5. LINQ to Objects
One requirement to using LINQ to Entities which is a superset of LINQ to SQL is Visual Studio 2008 SP1 beta. VS SP1 beta adds the Entity Framework support to Visual Studio. Another nice feature worth mentioning and slightly related to LINQ, is support for SQL Server 2008 via Server Explorer - which is neat.

Types of LINQ supported on the Compact Framework (v 3.5)

  1. LINQ to DataSets
  2. LINQ to Xml
  3. LINQ to Objects
Sadly no LINQ to SQL/Entities is supported as yet and there haven't been any announcements when and if it will be supported.

You can use SQL Metal to generate the .dbml file for LINQ to SQL - but this will only work for SQL Compact on the desktop not device.

Some good resources (not device specific though) can be found at:

http://weblogs.asp.net/scottgu/archive/2007/05/19/using-linq-to-sql-part-1.aspx

LINQ in Action: http://linqinaction.net/

Mike Taulty: http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/category/1001.aspx

Free Vista SP1 Support until 18th March 2009

Microsoft has announced free technical support on Vista SP1 until 18th March 2009. Telephone support is available worldwide. Some countries also offer chat and email support.

To find out more see here: http://support.microsoft.com/common/international.aspx?rdpath=1&prid=11274&gprid=500921

Derive from Exception not ApplicationException

A friend of mine Steve Gregson asked me the other day what exception class you should derive from when creating custom exceptions knowing that I would answer ApplicationException. I naturally told him ApplicationException, but I was wrong.

This post talks about this very issue with an excerpt from Jeffrey Richter and the reason...TargetInvocationException...or at least one of them. The original framework design was that applications would derive from ApplicationException and the CLR would derive from class Exception. This would then enable some code higher up the chain to distinguish the difference between an application exception over a CLR exception and handle it accordingly. But as a lot of framework classes didn't follow the ApplicationException pattern it renders this class useless as it is not possible to know where the exception came from (CLR or app) higher up the chain.

Best practices for handling exceptions can be found here.

In fact Krzysztof Cwalina's blog above co-authored this book:



I happen to own the above book and I just checked, page 197 where Jeffrey talks about not to use ApplicationException class. I highly recommend the above book by the way.

So there you have it, just forget ApplicationException ever existed...

Monday, July 28, 2008

LINQ to Objects on the Compact Framework - Part 2

I wrote an article recently about LINQ to Objects on the Compact Framework here.

This post goes into a bit more detail.

There are many examples out there in the communities in how to do simple queries, stuff like sorting filtering, grouping selecting new anonymous types etc. But what if we already have a relational schema that represents a one-to-one or a one-to-many mapping class hierarchy to our database schema which contains multiple records and we want to do some processing at a very low level in the hierarchy. Consider the schema defined below:



I've modelled this using SQL Server but I could have used quite a few different tools to do this.

The schema is quite nice to work with and it would be nice to have an object model that represented this in code.

In Part 1 we modeled the Customer table although notice in the above schema we have two new columns: HomePhone and MobilePhone so we will add these to our object model as follows:
public class CustomerInfo
{
private List<OrderInfo> orders = null;


public CustomerInfo()
{
orders = new List<OrderInfo>();
}

public int CustomerId
{
get;
set;
}

public string Name
{
get;
set;
}

public string AddressLine1
{
get;
set;
}

public string AddressLine2
{
get;
set;
}

public string AddressLine3
{
get;
set;
}

public string City
{
get; set;
}

public string County
{
get;
set;
}

public string PostalCode
{
get;
set;
}

public string Country
{
get;
set;
}

public string HomePhone
{
get;
set;
}

public string MobilePhone
{
get;
set;
}

public List Orders
{
get
{
return orders;
}
set
{
orders = value;
}
}
}
As you can see we now have the two additional properties added to support the additional columns and also we have an Orders property which gives us access to all orders for each customer.

Of course now we need both the Orders class the the Products class. They look like the following:
public class OrderInfo
{
public int OrderId
{
get; set;
}

public int CustomerId
{
get; set;
}

public int ProductId
{
get; set;
}

public int Quantity
{
get; set;
}

public ProductInfo Product
{
get; set;
}
}
Notice from the above we have the Product property which gives us the product this order relates to. Now we need the product class:
public class ProductInfo
{
public int ProductId
{
get;
set;
}

public string Description
{
get;
set;
}

public decimal Cost
{
get;
set;
}
}
That's it in terms of our object model. Nothing too complex. If we wanted, we could mark each class as Serializable so that if we wanted to serialize the data into binary we could (binary serializer is not supported on the CF) this is not required for XML serialization however. I do want to show an example of serializing this too as it adds an element of coolness to this example of how powerful we can make LINQ.

So now we need some data. Normally you'd write some SQL to populate your object model as I mentioned in previous posts sadly LINQ to SQL and LINQ to ADO.NET/Entitys are not supported. So this hard work still has to be hand cranked. Although there is an ORM toolset available for the Compact Framework. You can find this here. I'd be interested in hearing from anyone their experiences of this toolset as I haven't personally tried it, yet.

As I don't have a database, I'm just going to create an object model by hand with some fictitious data that I just made up. So this code looks as follows:
 List<CustomerInfo>customersSource = new List<CustomerInfo>
{
new CustomerInfo
{
CustomerId = 1,
Name = "Joe",
City = "London",
Orders = new List()
{
new OrderInfo()
{
OrderId = 1,
CustomerId = 1,
ProductId = 1,
Quantity = 5,
Product = new ProductInfo()
{
ProductId = 1,
Cost = 23.34M,
Description = "Garden tiles"

}
},
new OrderInfo()
{
OrderId = 2,
CustomerId = 1,
ProductId = 2,
Quantity = 10,
Product = new ProductInfo()
{
ProductId = 2,
Cost = 56.12M,
Description = "Grass seed"
}
},
new OrderInfo()
{
OrderId = 3,
CustomerId = 1,
ProductId = 2,
Quantity = 1,
Product = new ProductInfo()
{
ProductId = 3,
Cost = 599.95M,
Description = "Table and chairs"
}
}
}
},
new CustomerInfo
{
CustomerId = 2,
Name = "Pete",
City = "Paris"
},
new CustomerInfo
{
CustomerId = 3,
Name = "John",
City = "New York"
},
new CustomerInfo
{
CustomerId = 4,
Name = "Pete",
City = "London"
},
new CustomerInfo
{
CustomerId = 5,
Name = "Paul",
City = "Dublin"
},
new CustomerInfo
{
CustomerId = 6,
Name = "Steve",
City = "Exeter"
},
new CustomerInfo
{
CustomerId = 7,
Name = "Clare",
City = "Norwich"
}
};
So we have 2 orders for customer Joe (id 1) and no orders for any other customer.

Suppose now that we wanted a list of customers who have placed an order. I could code somthing like the following:
var customersWithOrders = from c in customersSource
where c.Orders.Count > 0
c.Name;


foreach (string s in customersWithOrders)
{
Debug.WriteLine(string.Format("Customer {0} has an order", s));
}
The output of above looks like:
Customer Joe has an order
Orders will always be non-null because we initialize a collection of orders in the CustomerInfo class. We could have coded the above using the new feature of C# 3.0 which is anonymous types. An example of using anonymous types is as follows:
var customersWithOrders = from c in customersSource
where c.Orders.Count > 0
select new
{
c.Name
};

foreach (var s in customersWithOrders)
{
Debug.WriteLine(string.Format("Customer {0} has an order", s.Name));
}
We could extend the anonymous type to include more information such as which city the customer is from:
var customersWithOrders = from c in customersSource
where c.Orders.Count > 0
select new
{
c.Name,
c.City
};
foreach (var s in customersWithOrders)
{
Debug.WriteLine(string.Format("Customer {0} from city {1} has an order",
s.Name, s.City));
}
We could also just select the customer like so:
var customersWithOrders =  from c in customersSource
where c.Orders.Count > 0
select c;
foreach (var s in customersWithOrders)
{
Debug.WriteLine(string.Format
("Customer {0} from city {1} country {2} has an order",
s.Name, s.City, s.Country));
}
The type is in fact a CustomerInfo object. It is implicitly inferred at runtime. The output of the above looks like the following:
Customer Joe from city London country  has an order
Notice how the country is blank, why is this? Go back to where we create our object model - we never intialized the Country property. In fact, as we are using auto-implemented properties the property will in fact be null. Cleverly though the code doesn't blow up because the above is in fact a bug.

You could from the var above do this:
CustomerInfo cust = customersWithOrders.First();
Which would give us our first CustomerInfo object found in the collection. The var is in this case an IEnumerable and First is an extension method on IEnumerable. There are many extension methods on IEnumerable which I will show examples of how to use lambda expressions in queries.

So far we have seen very simple queries. Now what if we wanted to do something more complex like find out which customers have placed orders totaling more than £100.00.

We use a new feature of C# 3.0 called lambda expressions to do this which makes our code very short and easy to read. Lambda expressions are much like anonymous methods as introduced with C# 2.0. There are plenty of resources out in the community that talk about lambda expressions in depth. In fact whenever you use LINQ - for example in the simple examples above the compiler will be creating lambda expression calculations in the background, seemlessly. Of course you can explicitly use them yourself to add extra power.

The code to achieve the above could look like the following:
var customersWithOrdersOver100 = from c in customersSource
where c.Orders.Sum(o => o.Product.Cost) > 100
select c.Name;
foreach (var s in customersWithOrdersOver100)
{
Debug.WriteLine(string.Format("Customer {0} has an order over £100.00", s));
}
The output of the above looks like:
Customer Joe has an order over £100.00
Here we have used extension method Sum on the IEnumerable interface then using lambda expression we are able to execute the query.

How cool is that, in just 3 lines of code - that is the impressive thing here.

That's it for this post, but in my next post Part 3 I talk about some more things you can do with LINQ.

Tuesday, July 22, 2008

LINQ to Objects on the Compact Framework - Part 1

I wrote an article recently about writing comparer classes to sort data on the CF because LINQ to SQL is not supported on the Compact Framework. I did forget to mention LINQ to Objects which *is* supported on the Compact Framework and of course adds so much more power than writing a comparer class.

So just as in the previous article we have a simple customer object:
public class CustomerInfo
{
public int CustomerId
{
get;
set;
}

public string Name
{
get;
set;
}

public string AddressLine1
{
get;
set;
}

public string AddressLine2
{
get;
set;
}

public string AddressLine3
{
get;
set;
}

public string City
{
get;
set;
}

public string County
{
get;
set;
}

public string PostalCode
{
get;
set;
}

public string Country
{
get;
set;
}
}
So instead of writing all that comparer logic, instead we can use LINQ to Objects as follows:
[MTAThread]
static void Main()
{
List<CustomerInfo> customersSource = new List<CustomerInfo>
{
new CustomerInfo {CustomerId = 1, Name = "Joe", City = "London"},
new CustomerInfo {CustomerId = 2, Name = "Pete", City = "Paris"},
new CustomerInfo {CustomerId = 3, Name = "John", City = "New York"},
new CustomerInfo {CustomerId = 4, Name = "Pete", City = "London"},
new CustomerInfo {CustomerId = 5, Name = "Paul", City = "Dublin"},
new CustomerInfo {CustomerId = 6, Name = "Steve", City = "Exeter"},
new CustomerInfo {CustomerId = 7, Name = "Clare", City = "Norwich"}
};

List<CustomerInfo> customersAscending =
(from c in customersSource
orderby c.City ascending
select c).ToList();

List<CustomerInfo> customersDescending =
(from c in customersSource
orderby c.City descending
select c).ToList();

Debug.WriteLine("Before sort");
DisplayCollection(customersSource);
Debug.WriteLine("");

Debug.WriteLine("After sort - Ascending");
DisplayCollection(customersAscending);
Debug.WriteLine("");
Debug.WriteLine("After sort - Descending");
DisplayCollection(customersDescending);

}

public static void DisplayCollection(List<CustomerInfo> customers)
{
foreach (CustomerInfo customer in customers)
{
Debug.WriteLine(string.Format("Name: {0}, City: {1}",
customer.Name, customer.City));
}
}
Output of the above looks like the following:
Before sort
Name: Joe, City: London
Name: Pete, City: Paris
Name: John, City: New York
Name: Pete, City: London
Name: Paul, City: Dublin
Name: Steve, City: Exeter
Name: Clare, City: Norwich
After sort - Ascending
Name: Paul, City: Dublin
Name: Steve, City: Exeter
Name: Joe, City: London
Name: Pete, City: London
Name: John, City: New York
Name: Clare, City: Norwich
Name: Pete, City: Paris
After sort - Descending
Name: Pete, City: Paris
Name: Clare, City: Norwich
Name: John, City: New York
Name: Joe, City: London
Name: Pete, City: London
Name: Steve, City: Exeter
Name: Paul, City: Dublin
So exactly the same results as both the IComparer and LINQ to SQL examples.

Of course LINQ is a little bit more powerful than traditional IComparer code in that we can filter data without having to write much code or go back to the database - which is quite powerful.

What if we wanted to show customers from London. We could write a query as:
 List<CustomerInfo> customersFromLondon = 
(from c in customersSource
where c.City == "London"
select c).ToList();
Debug.WriteLine(string.Format("Customers from london: {0} customers
found", customersFromLondon.Count.ToString()));
DisplayCollection(customersFromLondon);
The output of the above is as follows:
Customers from london: 2 customers found
Name: Joe, City: London
Name: Pete, City: London
Bearing in mind these examples are only 1 dimensional objects, you can see the power when we have related objects. I will show some more complex examples in another post.

We can also do wildcard filtering such as show me customers that name begins with 'J':

List<CustomerInfo> customersNameStartsWithj =
(from c in customersSource
where c.Name.StartsWith("J")
select c).ToList();
Debug.WriteLine(string.Format("Customer that name begins with the letter 'J': {0}
customers found", customersNameStartsWithj.Count.ToString()));
DisplayCollection(customersNameStartsWithj);
The output of this is as follows:
Customer that name begins with the letter 'J': 2 customers found
Name: Joe, City: London
Name: John, City: New York
Which gives us two results as with have a John and a Joe in our list of data.

Typically though, that list would normally have been returned from a data layer in your application and instead of going back to the database to filter, sort etc, you'd use LINQ instead which will improve performance no-end. This is also quite powerful if you are retrieving your data from a remote server, you wouldn't want to do another round trip to filter or sort results.

The really exciting thing about all this code is that it will run against your existing custo strongly typed collections and that is very powerful.

UPDATE: See part 2 of this series here.

Sunday, July 20, 2008

Building my Windows Vista Ultimate machine

***Not related to mobility or in fact anything else technical but I'd thought I'd post my experience here for any hardware geek readers out there! :)

I decided recently to build a Windows Vista machine to use on my network LAN at home and I thought I'd share my experiences here. This should have been a fairly simple task but turned out to be a bit of a nightmare but I finally got through it..... in the end.

The spec of this machine is as follows:

Motherboard: Gigabyte GA-P35-DQ6 (New Intel P35 chipset)
Processor: Intel Core 2 Duo E8200 2.66GHz (45nm) (not overclocked, not yet anyway)
Memory: Corsair 2GB DDR2 800 MHz/PC2-6400 XMS2 Non-ECC
HDD: Samsung Spinpoint 500GB
CD/DVD Drive: HP 1040d
Case: Coolermaster Elite 330 with Coolermaster eXtreme Power 460w PSU
Monitor: Samsung SyncMaster 2032BW (20" LCD)
Wireless: Netgear WG311T 108mbps Wireless PCI
Graphics: NVIDIA GeForce 8500 (512mb)
Sound: On board 8 channel (not bad at all for on-board)
Keyboard: KeySonic 2.4Ghz Wireless Compact Keyboard with Integrated Touchpad
Mouse: Acme laser something....need to get a decent mouse.
Web cam: Microsoft LifeCam 6000
Media Centre Support: Microsoft OEM Media Centre Remote Control (for controlling Media Centrer Extender - XBox 360)
OS: Windows Vista Ultimate 64-bit SP1

The good the bad and the ugly:

1. Ugly: The first thing was to check the monitor didn't have any dead pixels. So I simply hooked this upto my ancient laptop, luckily the ancient laptop has a VGA port and the cool Samsung supports VGA! Now did the monitor have any dead pixels,Guess what, it did. This is unusal for Samsung, they have a zero dead pixel policy even though PC World told me otherwise! Having said this PC world replaced the deal pixel monitor with no problems.

2. Ugly: When the last component arrived from ebuyer the Coolermaster Elite 330 with the processor I was finally ready to build this machine. After several hours of putting it all together it failed to boot, just wouldn't turn on. I figured it might be a lack of power because the board is a bit of a beast. So I hooked up the additional 2x4 12v power connector in hope the board will have enough power from the PSU. And it turned on! but it just kept on rebooting. You could hear the HDD kick in, fans etc and before any output was displayed on the screen it would reboot as if it was in an endless loop.

After a bit of diagnostics, I found out there was a CMOS checksum error, so I decided to try and short (reset) the CMOS to see if that fixed the problem. It didn't, sadly. The motherboard was then sent back to the supplier for repair as I bought this motherboard more than 6 months earlier I didn't get a new one. I just hadn't had the time to built it.

The fixed motherboard arrived while I was away around 2 weeks later so I was quite excited that I might have a working board and moments away from a running system as these days building PC's is much easier than they used to be. So lets start again!


The board waiting to be unpacked.


It's a very nice looking board.

1. In the picture above, I've added the CPU and the memory (memory in dual mode as the board supports it). The stock fan was very difficult to install. Today these fans come with these springy type mounts that click into place. They no longer just stick onto of the CPU as they need very tight contact with the CPU to disperse as much heat as possible - multi-core CPUs run very hot. Getting all mounts through the motherboard was quite a task! You also need some conducting lube to help with heat flow.


The case with the side off, ready for the board to be inserted

The case comes with a 120mm stock fan to draw heat from the CPU. It doesn't have any BIOS regulator as it doesn't connect to the motherboard so there is no way to control the speed of it. This is unlike the CPU fan. All we have to do is hook it up to one of the power cables.


The 120mm fan extracing heat from the CPU and the north bridge

We do not have to install the PSU as it comes pre-installed.

2. Before we insert the board we will need to install the HDD so it makes it easier to get it in before we install the motherboard.

This case (Coolermaster Elite 330) contains the quick release drive sockets so we do not have to use a screwdriver - this feature I really liked. The case has another similar features for expansion cards. But to be honest, I used screws in addition to using the quick release gadget in the case as it didn't seem to secure the cards well enough in place for my likeing.


Samsung Spinpoint 500Gb HD501LJ


Hard drive installed

3. Now its time to install the CD ROM drive. Before we do this we need to prepare a bay for the drive to be installed.


The front of the case and the blank removed


The drive inserted - HP 1040d

The drive will not be flush against the case here, it will be flush againt the front of the case which is why it's sticking out a bit.

4. Now we need to hook up the IDE and power cables to the drive as it's an IDE drive.





5. Now we can install the motherboard into the case.


Board inserted

Now we have the board inserted. The GA-P35-DQ6 is an ATX board so just fits without the hard disk drive touching any part of the board.

6. Now its time to install the graphics card.


NVIDIA GeForce 8500 (512mb)

This graphics won't run a games like Crysis or COD4 on full graphics but I didn't build it for gaming, my xbox is for gaming.


Graphics installed into PCI-E x16 slot also the SATA HDD hooked up to the board

The case would support a much larger card such as the 8800 series without problems.

7. Now install the wireless NIC card.


Netgear WG311T


NIC card installed into the motherboard

8. Now we need to put the front of the case on.



9. We now need to install the external SATA blanking plate into the case and hook it up to the board. This enables us to plug in external HDD drives without somesort of caddy case.


The plate to install


The plate installed into the board

10. We will need to install additional USB ports. I do not have any photos of doing this but it's a simple case of installing a plate and screwing it into place and hooking it up to the motherboard.

11. There are a couple of finishing touches required to complete this build. Hooking up the USB the headphone and MIC ports available on the front of the case to the board. Note in the picture below, we have one spare 2.5" bay. This can either be a floppy drive, or a compact flash multiple support card reader which I will probebly buy as this feature would be very useful.



12. Hook up the monitor to the graphics card (DVI support).


The card also supports VGA and S-Video

You can see in the above picture the additional USB ports we installed. 12 in total!


The DVI cable in the back of the monitor

13. Bluetooth support. I will use my bluetooth card - a gift from Microsoft :) This is a simple case of plugging it into one of the spare USB ports.




14. Installing the webcam. Again this is a simple case of plugging it into a spare USB port.



Microsoft LifeCam 6000

And that is it. Finished. All that is required now is installation of all the software!


The finished article


Samsung SyncMaster 2032BW (20" LCD)



A couple of points to make. 2 GB of RAM is not enough. Vista sits idle using around 50% load on 2GB so as soon as you start hammering it, remote desktop sessions, Visual Studio etc the memory usage increases upto around 70% which is when Vista becomes a little unstable. I have ordered some more memory to cope with this.

The board supports dual memory too which helps on throughput. I don't recommend the KeySonic wireless 2.4 Ghz keyboard as it seems to disconnect on inactivity which then requires extra key presses to "wake it up".

The next machine I am building is a Windows Server 2008 box, I will post my experience of that here!