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.