Thursday, May 15, 2008

Sending an ICMP packet to a remote server on the CF (Ping)

This question has been coming up a lot recently in the community "How do I ping a remote server from a device?".

I thought I'd write this quick article with some code in how to do this.

Even if you are using the latest version of the Compact Framework which is 3.5, there is no support for this without getting your hands dirty with native code.

A ping is essentially an Internet Control Message Protocol (ICMP) message and can be achieved from managed code by P/Invoking IcmpSendEcho2. This function can be found in Iphlpapi.dll and this call is supported on Windows CE 4.1 and later. For previous versions you'll need to link in library Icmplib.lib.

NOTE: If the machine is sitting behind a router (usual case), some routers give the option for blocking ICMP protocol, if the call doesn't work, you might want to check your router first.

Now this is the good part, instead of having to code the call to the IcmpSendEcho2 function yourself, OpenNETCF has kindly already provided a managed wrapper for us. The OpenNETCF Ping class can be found in the OpenNETCF.Net.NetworkingInformation namespace. Something like the following should work:


public static PingReply Ping(string ipAddress)
{
Cursor.Current = Cursors.WaitCursor;
Ping ping = new Ping();
PingReply reply = null;
try
{
reply = ping.Send(ipAddress, 10000);
switch (reply.Status)
{
case IPStatus.Success:
Cursor.Current = Cursors.Default;
if (ipAddress.IndexOf(".") > -1)
{
MessageBox.Show(string.Format(Properties.Resources.PingServerOK,
ipAddress,
reply.RoundTripTime),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Asterisk,
MessageBoxDefaultButton.Button1);
}
else
{
MessageBox.Show(string.Format(Properties.Resources.PingServerOK2,
ipAddress,
reply.RoundTripTime,
reply.Address.ToString()),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Asterisk,
MessageBoxDefaultButton.Button1);
}
break;
case IPStatus.BadDestination:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingBadDestination,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.BadOption:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingBadOption,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.BadRoute:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingBadRoute,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.DestinationHostUnreachable:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingDestinationHostUnreachable,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.DestinationNetworkUnreachable:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingDestinationNetworkUnreachable,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.DestinationPortUnreachable:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingDestinationPortUnreachable,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.DestinationProhibited:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingDestinationProhibited,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;

case IPStatus.DestinationScopeMismatch:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingDestinationScopeMismatch,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.DestinationUnreachable:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingPingDestinationUnreachable,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.HardwareError:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingHardwareError,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.IcmpError:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingIcmpError,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.NoResources:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingNoResources,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.PacketTooBig:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingPacketTooBig,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.ParameterProblem:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingParameterProblem,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.SourceQuench:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingDestinationScopeMismatch,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.TimedOut:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingTimeout,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.TimeExceeded:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingTimeExceeded,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.TtlExpired:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingTtlExpired,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.TtlReassemblyTimeExceeded:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingTtlReassemblyTimeExceeded,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.Unknown:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingUnknown,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
case IPStatus.UnrecognizedNextHeader:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingUnrecognizedNextHeader,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
default:
Cursor.Current = Cursors.Default;
MessageBox.Show(string.Format(Properties.Resources.PingUnknown,
ipAddress),
Properties.Resources.Ping,
MessageBoxButtons.OK,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
break;
}
}

catch (PingException ex)
{
Cursor.Current = Cursors.Default;
DialogResult result = MessageBox.Show(ex.Message,
Properties.Resources.Ping,
MessageBoxButtons.RetryCancel,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
if (result == DialogResult.Retry)
Ping(ipAddress);
}
catch (Exception ex)
{
DialogResult result = MessageBox.Show(ex.Message,
Properties.Resources.Ping,
MessageBoxButtons.RetryCancel,
MessageBoxIcon.Hand,
MessageBoxDefaultButton.Button1);
if (result == DialogResult.Retry)
Ping(ipAddress);
}
finally
{
Cursor.Current = Cursors.Default;
}
return reply;
}
That's all there is too it!

Dropping Defaults (Column Constraint) in SQL Compact 3.5

This interesting post talks about the DEFAULT SQL statement is no longer a database constraint in SQL Compact 3.5. You merely use just DEFAULT.

To remove a default simply use SQL syntax: ALTER TABLE table ALTER COLUMN col DROP DEFAULT. This has always been the recomended way to remove defaults.

See here for more info: http://blogs.msdn.com/sqlservercompact/archive/2008/04/03/dropping-defaults.aspx

Tuesday, May 13, 2008

Visual Studio 2008 and .NET Framework 3.5 Service Pack 1 Beta

Visual Studio 2008 and .NET 3.5 SP1 beta is now available and been officially released.

VS 2008 SP1: http://download.microsoft.com/download/7/3/8/7382EA08-4DD6-4134-9B92-8585A5B07973/VS90sp1-KB945140-ENU.exe

.NET 3.5 SP1: http://download.microsoft.com/download/8/f/c/8fc1fe13-55de-4bf5-b43e-375daf01452e/dotNetFx35setup.exe

Express with SP1:

  1. http://download.microsoft.com/download/F/E/7/FE754BA4-140B-413C-933F-8D35FB150F12/vbsetup.exe
  2. http://download.microsoft.com/download/F/E/7/FE754BA4-140B-413C-933F-8D35FB150F12/vcsetup.exe
  3. http://download.microsoft.com/download/F/E/7/FE754BA4-140B-413C-933F-8D35FB150F12/vcssetup.exe
  4. http://download.microsoft.com/download/F/E/7/FE754BA4-140B-413C-933F-8D35FB150F12/vnssetup.exe

TFS 2008 SP1: http://download.microsoft.com/download/a/e/2/ae2eb0ff-e687-4221-9c3e-9165a942bc1c/TFS90sp1-KB949786.exe

Note: This release does *not* include SP1 for .NET CF 3.5. No announcements have been made when this will occur.

Scott Guthrie talks about some interesting features/fixes with the advent of this SP.

Wednesday, May 07, 2008

Every Developer, Now a Mobile Developer!

The Mobile Developer Group at Microsoft has just recently created this blog: http://blogs.msdn.com/mobiledev/default.aspx

There is not much on there as yet, but book mark or subscribe via RSS as I'm sure the group will release some interesting stuff to read.

Sunday, May 04, 2008

Bring back XP Alt-Tab in Windows Vista!

This post hits the nail on the head with regards to the problem with the new Alt-Tab in Windows Vista. Win-Tab is quite cool and useful in which I will eventually move over to. But I want something fast and something that I can quickly find,hmmm much like in XP. In Vista, when pressing Alt-Tab instead of showing the icon of the application as in pre-Vista, this has been replaced with a very small image of the whole window which is too small to identify.

To restore the XP Alt-Tab functionality simply add a DWORD named AltTabSettings to HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer and set its value to 1.


Friday, May 02, 2008

Rethrowing Exceptions in .NET

I recently learnt after all this time using .NET that rethrowing an exception such as throw ex in a catch block causes the CLR to alter the original exception meaning you will not get the full trace stack in the exception when it is finally bubbled up the chain.

Consider the following code:

class Program
{
static void Main(string[] args)
{
MyClass myClass = new MyClass();
myClass.Foo(true);
}
}

public class MyClass
{
public void Foo(bool includeTraceStack)
{
if (includeTraceStack)
ThrowExceptionIncTraceStack();
else
ThrowExceptionNotIncTraceStack();
}

public void ThrowExceptionIncTraceStack()
{
int a = 0;
int b = 0;

try
{
Divide(a, b);
}
catch (DivideByZeroException)
{
throw;
}
}

public void ThrowExceptionNotIncTraceStack()
{
int a = 0;
int b = 0;
try
{
Divide(a, b);
}
catch (DivideByZeroException ex)
{
throw ex;
}
}

public decimal Divide(int a, int b)
{
return a / b;
}
}
Calling MyClass.Foo(true) gives you the following trace stack:
 at ConsoleApplication1.MyClass.Divide(Int32 a, Int32 b) in C:\Develop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 58
at ConsoleApplication1.MyClass.ThrowExceptionIncTraceStack() in C:\Develop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 38
at ConsoleApplication1.MyClass.Foo(Boolean includeTraceStack) in C:\Develop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 22
at ConsoleApplication1.Program.Main(String[] args) in C:\Develop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 13
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
Calling MyClass.Foo(false) gives you the following trace stack:
at ConsoleApplication1.MyClass.ThrowExceptionNotIncTraceStack() in C:\Develop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 52
at ConsoleApplication1.MyClass.Foo(Boolean includeTraceStack) in C:\Develop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 24
at ConsoleApplication1.Program.Main(String[] args) in C:\Develop\ConsoleApplication1\ConsoleApplication1\Program.cs:line 13
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
Notice when throwing the original exception by qualifying the exception ie: throw ex as opposed to just throw clears the trace stack lower down in the calling chain. Just using throw gives us more information in the trace stack.

I must say I have always coded thow ex for clarity, not anymore!

Thursday, May 01, 2008

Mobile Client Software Factory

You might remember, last year I wrote a couple articles regarding the Mobile Software Client Factory Data Access Application Block (which I still use today) and how to use them etc These articles can be found here: Part1 and Part2.

It turns out Microsoft is no longer developing the MCSF but it can be found on codeplex here.

Wednesday, April 30, 2008

SQL Server CE 3.0 version inconsistencies

Many people in the community are reporting inconsistencies when viewing the version of SQL Server CE 3.0 through File Explorer file properties and Visual Studio Add Reference dialog.

I use SQL Server Compact 3.5 (note the naming differences) now which seems to be OK in terms of versioning. But earlier versions (3.0, 3.1) don't. See the screen shots attached.


As per Visual Studio.


As per the file system.

This diference is related to the build process between Mobile and Desktop assemblies. Microsoft has partially fixed SQL Server CE 3.1 but fully fixed SQL Server Compact 3.5.