OpenID and ASP.NET web sites
14 April 08 11:21 PM | andarno | 0 Comments   

If you are a web developer I hope you've considered accepting OpenID credentials for logging in your users.  If you have an ASP.NET web site, the process of adding OpenID support to your web site couldn't be easier when you use the free C# DotNetOpenId library. 

Supporting OpenID is a great idea:

  1. No more "I forgot my username/password" pages.
  2. No more "Change Password" pages.
  3. Your users have one less username/password to make-up/reuse when they join your site.
  4. Users who prefer greater security than a username/password provides can choose an OpenID Provider that provides that assurance, without you having to enhance your site.
  5. It's free.  And so are the libraries that you can drop in to add support for it.
Filed under: ,
Why you should never use TCHAR in C++ header files
22 February 08 11:38 PM | andarno | 0 Comments   

In C++, TCHAR, LPCTSTR, CString, and other types discretely change to their char/WCHAR, CStringA/CStringW counterparts depending on whether UNICODE is defined in your source code.  Cool.  By conscientiously using _countof(x) instead of sizeof(x) where appropriate and TCHAR's everywhere instead of char/WCHAR, you can write code that will compile with ANSI or UNICODE support as easy as flipping a switch.  But these tricks are not safe in .h header files.

Consider the following very simple scenario:

tool.h:

void SayHello(CString recipient);

tool.cpp

//#define UNICODE
#include "tool.h"
void SayHello(CString recipient)
{
   printf(_T("Hello, %s"), recipient);
}

Compiled into a standalone app, these two files would easily compile with or without the UNICODE symbol set and the right thing would happen. 

But now consider that you compiled this not into a standalone .exe but into a .lib or .dll, and shared out the .h header file with some consuming application (which might be an external customer).  That header file will be #include'd into their app, which may or may not define the UNICODE symbol.  Suppose you defined UNICODE and the linking app did not.  The header file will automatically adjust to the consuming app's ANSI style strings and the C++ compiler and linker will be perfectly happy. You won't know there's a problem with the app passing ANSI strings to your UNICODE library until runtime when you get random data corruption and application crashes.

How do you avoid this?  Well there are two ways, really.  If you already have an extensive library written with header files that use TCHARs and their cousins, just compile and ship both a UNICODE and an ANSI version of your library (toolA.lib and toolU.lib).  Then instruct your customer to link to the correct one. 

But if you're writing a library from scratch, consider using all strongly-typed ANSI or UNICODE character types in your .h files, and in the method signatures of your .CPP files.  Then use whatever generic TCHAR's make sense within the implementation of your methods.  So for example:

tool.h:

void SayHello(CStringW recipient);

tool.cpp

//#define UNICODE
#include "tool.h"
void SayHello(CStringW recipient)
{
   USES_CONVERSION;

   wprintf(L"Hello, %s", recipient);

   // Call some ATL/MFC function that is only available in TCHARs
   TCHAR szBuffer[] = _T("Some TCHAR buffer");
   SomeImplementationSpecificMethod(szBuffer, CW2T(recipient));
}

This will compile with or without the UNICODE symbol defined, but the method signature will always be UNICODE, and the internals of method implementation will automatically adjust as appropriate for UNICODE or ANSI support.

Filed under:
Why your Visual Studio add-in fails to load with error 0x80004005
15 February 08 04:43 PM | andarno | 0 Comments   

Writing Visual Studio add-ins gives you the power to do some impressive things within the IDE to enhance your productivity.  Unfortunately getting your add-in to load into Visual Studio can sometimes be a pain.  Here's just a tip you should know about the .AddIn file that Visual Studio generates for you.

The path you provide to the add-in assembly .dll should be relative to the location of the .AddIn file itself.  Absolute paths do not work.  If you try using one, you get an unhelpful error dialog that mentions error code 0x80004005.

Hopefully we can get some more useful error messages in future versions of Visual Studio.

Filed under:
Why double-clicking on an .SLN file doesn't always launch Visual Studio
13 February 08 08:32 PM | andarno | 4 Comments   

Well this is my first post as a member of the Visual Studio Platform & Ecosystem team.  My second full day on the job, and I've found some less-than-ideal areas of Visual Studio that I'm eager to improve for myself and for you, our customers.  How exciting.  I'll do my best to post to my blog whenever I run across some unexpected scenario that is likely to hit customers and how to work around it.

So on to the first scenario...

Scenario

You have Visual Studio 2008 installed, but some Visual Studio Solution files (.sln) don't open when you double-click on them.  In fact, nothing happens.

Analysis

When you double-click on a .sln file, a small program called vslauncher.exe is called with the path to your .sln file as its first argument.  The job of vslauncher.exe is to read enough of your .sln file to determine which version of Visual Studio to use to open your file.  Since you may have several versions of Visual Studio installed (VS2003, 2005, 2008, Express SKUs, etc.) you probably want to open the solution with the same copy of Visual Studio that you used to create it.

The issue comes up when your .sln file signature (the first two lines of your file) don't match any known release of Visual Studio.  Visual Studio 2008 .sln files typically start with these two lines:

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008

But suppose you have a .sln file that was created with a Visual Studio 2008 beta.  You might have a .sln file that starts with this instead:

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio Codename Orcas

When Visual Studio 2008 RTM reads this, it doesn't recognize its earlier beta versions' solution file signatures and just quits.  It would be nice if a user-friendly message telling what happened would pop up.  Alas, it does not.

Workaround

Simply open your .sln file in Notepad and change "Codename Orcas" to "2008" on the second line. 

[Update 2/16/08] This blog was featured on Channel 9, and as a result a couple of people wrote up a PowerShell script and a C# program to automate updating all your .sln files at once.  Very cool.

Filed under:
Write a NetCF app that provisions your device from your device
04 February 08 07:42 AM | andarno | 1 Comments   

My first post in the provisioning series introduced what provisioning of your Windows Mobile device means and the basics of why you would want to do it.  In future posts I'll give some concrete examples of XML that accomplish a few common tasks.  But what good are XML examples if you can't try them out on your device or emulator?  It would be much better if you had an app that would let you write in XML and see what happens when you send it to the provisioning API of Windows Mobile.  I'd like to introduce you to a sample I wrote just for this purpose. 

The open-source Windows Mobile Provisioning tool is a .NET Compact Framework application that exposes provisioning directly to the user.  It will take strings of XML and pass them directly to Windows Mobile and return the resulting XML.  It also has an interface to help you set a proxy server and add domains to the Work network and crafts the provisioning XML for you. 

Over time I may add additional built-in provisioning scenarios to the sample.  But for now, download it, compile it, and try it out.  Let me know what you think, either here or on the Discussions page for this sample.

Microsoft launches MSDN Code Gallery for open-source sharing
01 February 08 03:28 PM | andarno | 0 Comments   

Microsoft recently launched the MSDN Code Gallery for Microsoft employees and others to share code and software using the Microsoft Public License.  MSDN Code Gallery looks and feels very much like CodePlex, but Code Gallery lacks the TFS source control feature and developers can only publish their source code and binaries as releases.

As a Microsoft employee, I am happy to see this site launch as it finally gives us an easy way to publish more source code from our blogs.  Watch my blog for a new device provisioning app that I will publish soon.

New job with Visual Studio Platform & Ecosystem
24 January 08 09:55 PM | andarno | 0 Comments   

I have accepted a new position within Microsoft with the Visual Studio Platform & Ecosystem team.  Rather than working on the .NET Compact Framework I will be working on "VS10". 

I don't actually make the transition for a few more weeks, so I look forward to several more posts on NetCF topics before leaving.

XmlSerializer compatibility between NetCF and the desktop
23 January 08 09:28 PM | andarno | 0 Comments   

The XmlSerializer found in the .NET Compact Framework has a very different implementation than the one found in the full .NET Framework.  The reasons for the differences include size and performance constraints that make the desktop's XmlSerializer inappropriate for devices. 

As a result of the different implementation, there are inevitably going to be differences between how each serializer turns objects into XML and back again.  We try to minimize these differences so that your C# code will run on NetCF and desktop in a very portable way.  When we find differences, we have to weigh the consequences of possibly breaking backward compatibility with applications written for earlier versions of NetCF against breaking compatibility with desktop for current and future applications.

One area where cross-platform compatibility becomes particularly important is in web services.  When you generate web service proxies for your NetCF applications using Visual Studio's Add Web Reference function (or the command line wsdl.exe, xsd.exe, or NetCFSvcUtil.exe tools) you expect those proxies to serialize to XML that the service is expecting.  These tools are generally developed and tested against the desktop's version of the XmlSerializer more thoroughly than against the NetCF XmlSerializer.  So any differences in how NetCF's XmlSerializer works could cause incompatible xml to be sent to a service.

As I have been code reviewing .NET Compact Framework's version of the serializer and fixing bugs in it, I've come across a number of differences in how the two serializers behave that can affect web services.  Some of these fixes made it into NetCF 2.0 SP2.  Many more made it into NetCF 3.5.  But quite a few issues were discovered too late to fix in the NetCF 3.5 release. 

In an upcoming NetCF release there are likely to be some breaking changes to the way the XmlSerializer works in order to improve web service and desktop compatibility.  This is likely to only affect applications that use multiple namespaces in a single type that is being serialized as that is where most of the bugs seem to be.

You can prepare for these changes by testing your application on the full .NET Framework today.  If it runs correctly there, then it will probably run correctly on a future version of the .NET Compact Framework. If you notice that the XML the desktop produces is different than what NetCF produces, you will probably need to adjust your code so that it produces the desired XML on the full Framework, and consider releasing a service pack for your own application to your customers who are using new versions of NetCF.

A full list of breaking changes is published with each release of the .NET Compact Framework.  You should review those if your NetCF app uses the XmlSerializer.

Filed under: ,
Using the XmlSerializer as an XmlObjectSerializer with WCF
18 January 08 08:06 PM | andarno | 0 Comments   

The Windows Communication Foundation introduced the new DataContractSerializer to replace the XmlSerializer in many scenarios.  The DataContractSerializer is faster than the XmlSerializer, but has certain limitations.  For example it does not support serializing an object's members as XML attributes.  When you write a WCF client that calls a service, WCF will generate a proxy class that defaults to using the DataContractSerializer and falls back to the XmlSerializer if the service uses features that the DataContractSerializer does not support.  This fallback mechanism is automatic if you're using WCF's service model.

But if you're working at WCF's messaging layer, you must explicitly choose which serializer to use.  You can use any serializer that derives from System.Runtime.Serialization.XmlObjectSerializerSystem.Runtime.Serialization.DataContractSerializer already derives from this class -- but System.Xml.Serialization.XmlSerializer does not.  So you have some work to do before you can use the XmlSerializer as your serializer of choice at the messaging layer of WCF.

In order to use the XmlSerializer to send messages with WCF, we have to define a class that derives from XmlObjectSerializer and uses the XmlSerializer internally.  In essence we will create a wrapper class for the XmlSerializer that makes it look and work like an XmlObjectSerializer.  XmlObjectSerializer is an abstract class with only a few methods we need to implement. 

Below is a sample of a class that will do just that.

namespace Microsoft.ServiceModel.Samples
{
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Text;
    using System.Xml;
    using System.Xml.Serialization;
    using System.Runtime.Serialization;

    public class CFMessagingSerializer : XmlObjectSerializer
    {
        readonly Type objectType;
        XmlSerializer serializer;

        public CFMessagingSerializer(Type objectType)
            : this(objectType, null, null)
        {
        }
        
        public CFMessagingSerializer(Type objectType, string wrapperName, string wrapperNamespace)
        {
            if (objectType == null)
                throw new ArgumentNullException("objectType");
            if ((wrapperName == null) != (wrapperNamespace == null))
                throw new ArgumentException("wrapperName and wrapperNamespace must be either both null or both non-null.");
            if (wrapperName == string.Empty)
                throw new ArgumentException("Cannot be the empty string.", "wrapperName");
            
            this.objectType = objectType;
            if (wrapperName != null)
            {
                XmlRootAttribute root = new XmlRootAttribute(wrapperName);
                root.Namespace = wrapperNamespace;
                this.serializer = new XmlSerializer(objectType, root);
            }
            else
                this.serializer = new XmlSerializer(objectType);
        }

        public override bool IsStartObject(XmlDictionaryReader reader)
        {
            throw new NotImplementedException();
        }

        public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName)
        {
            Debug.Assert(serializer != null);
            if (reader == null) throw new ArgumentNullException("reader");
            if (!verifyObjectName)
                throw new NotSupportedException();

            return serializer.Deserialize(reader);
        }
        
        public override void WriteStartObject(XmlDictionaryWriter writer, object graph)
        {
            throw new NotImplementedException();
        }

        public override void WriteObjectContent(XmlDictionaryWriter writer, object graph)
        {
            if (writer == null) throw new ArgumentNullException("writer");
            if (writer.WriteState != WriteState.Element)
                throw new SerializationException(string.Format("WriteState '{0}' not valid. Caller must write start element before serializing in contentOnly mode.",
                    writer.WriteState));
            using (MemoryStream memoryStream = new MemoryStream())
            {
                using (XmlDictionaryWriter bufferWriter = XmlDictionaryWriter.CreateTextWriter(memoryStream, Encoding.UTF8))
                {
                    serializer.Serialize(bufferWriter, graph);
                    bufferWriter.Flush();
                    memoryStream.Position = 0;
                    using (XmlReader reader = new XmlTextReader(memoryStream))
                    {
                        reader.MoveToContent();
                        writer.WriteAttributes(reader, false);
                        if (reader.Read()) // move off start node (we want to skip it)
                        {
                            while (reader.NodeType != XmlNodeType.EndElement) // also skip end node.
                                writer.WriteNode(reader, false); // this will take us to the start of the next child node, or the end node.
                            reader.ReadEndElement(); // not necessary, but clean
                        }
                    }
                }
            }
        }

        public override void WriteEndObject(XmlDictionaryWriter writer)
        {
            throw new NotImplementedException();
        }
        
        public override void WriteObject(XmlDictionaryWriter writer, object graph)
        {
            Debug.Assert(serializer != null);
            if (writer == null) throw new ArgumentNullException("writer");
            serializer.Serialize(writer, graph);
        }
    }
}
Filed under: , ,
An impressive Silverlight Slideshow control
01 January 08 03:31 PM | andarno | 3 Comments   

I recently discovered a very impressive (and free) Silverlight slideshow control. I would recommend it over any Flash-based slideshow controls that I've seen so far (because this one's better--not just because I work here <g>)

Here's a sample:

Watch for a follow-up post on my JMPInline blog on the tricks I had to pull to get this Silverlight control working on Blogger while still pulling RSS photo feeds from PicasaWeb, despite the XSS-limitations imposed on Silverlight 1.0 controls.

Filed under:
.NET Compact Framework 3.5 Power Toys RTW
10 December 07 10:49 PM | andarno | 0 Comments   

The .NET Compact Framework 3.5 Power Toys have been released.  You can download them from here.

Tools included in this package:
Remote Performance Monitor and GC Heap Viewer – Provides real time counter data (ranging from Garbage Collector activity to type loading info) on a running NETCF application. The GC Heap Viewer feature allows you to capture the managed heap at any moment your app is running to view live references, and allows you to compare multiple snapshots to find memory leak issues.
NETCF CLR Profiler – CLR Profiler is an instrumenting allocation profiler for NETCF applications. It provides detailed allocation visualizations, allocation callstacks visualizations and useful for diagnosing memory management issues.
App Configuration Tool (NetCFcfg.exe) - On-device tool for specifying what version of the NETCF runtime an application will run against, displaying installed versions of NETCF and displaying info about DLLs in the GAC.
NETCF ServiceModel Metadata Tool – The .NET Compact Framework ServiceModel Metadata Tool (netcfsvcutil.exe) allows you to generate a Windows Communication Foundation (WCF) client proxy to help developers consume WCF services on device. Like svcutil.exe, which is the desktop version of the utility, netcfsvcutil.exe is a command-line tool that generates service model code from metadata documents and generates metadata documents from service model code.
Remote Logging Configuration Tool – The Logging Configuration Tool enables users to easily configure logging options on a NETCF device including: loader, interop, network, error and finalizer logs.
NETCF Network Log Viewer – A utility for viewing NETCF network log data.

Filed under: , ,
My favorite Live Writer plug-ins
01 December 07 04:51 PM | andarno | 2 Comments   

Disclaimer: some or all of these plug-ins are not Microsoft created or endorsed, and my pose a security risk by installing them without verifying their source.

  1. Paste From Console - automatically formats and colorizes text copied from a console window for easy viewing on your blog. Paste from Visual Studio - pastes code copied from Visual Studio and preserves Visual Studio's coloring.
  2. Insert Code - colorizes code you type appropriately for several common languages.
  3. Flickr4Writer - inserts a picture to your blog from your flickr account, while keeping the picture hosted on flickr.
  4. Picasa Image Plugin - inserts pictures and slideshows hosted on PicasaWeb.
  5. Insert from SkyDrive - inserts links to your SkyDrive files into your posts.
Provisioning smart devices: an introduction
29 November 07 04:19 PM | andarno | 0 Comments   

A lot of questions on the MSDN forums center around how to customize a device or an app to prefer a specific network connection over another.  The correct way to accomplish this is by "provisioning" the device.  In fact a great many aspects of the device and the applications on it can be configured through 'provisioning' the device. 

Since provisioning is so broad, I've just started a tag for this topic, and I expect to post several times to this blog in the next little while on how to accomplish several common tasks through provisioning the device.  But because the topic is so broad, I want to give a brief overview of what it is, and point you to several MSDN resources that can help you get started. 

You should be warned that provisioning a device incorrectly can wreak havoc with whatever system you altered in the process.  This means your cell phone or data plan could quit working, or your data plan will become the preferred network over wi-fi and cost you lots more money, or that all your browser favorites will disappear.  In short, lots of bad things can happen so you should consider testing on a device emulator before doing anything on a physical device.

To provision a device, you send an XML document to the device via the device's DMProcessConfigXML native function or remotely from a desktop computer via the ProvisionDevice(...) method on the Microsoft.SmartDevice.Connectivity.Device managed class.  The .NET Compact Framework offers no managed wrapper to the DMProcessConfigXML function, so if you want to provision your device from an application running on the device itself you will have to P/Invoke into this function on your own.  It turns out that doing this properly is tricky and deserves a post of its own (stay tuned).  But if you're eager to get started check out the P/Invoke.Net page on the function that I recently added.

The XML document you send to the device is carefully crafted to either set or query parameters of some characteristic of the device.  These characteristics may relate to your browser favorites list, the network connections available, the proxy server(s) available for use, speed dial settings, sounds, ActiveSync, etc. etc.  The full list is available from MSDN: Configuration Service Provider Reference for Windows Mobile-Based Devices.  For the purposes of NetCF-related efforts however, on this blog I will focus primarily on the network-related configuration settings you can reach through provisioning.

In response to the XML document you send to the device, you get another XML document with the results of your query.  If you were just setting parameters, you essentially get the same XML document back.  If you were querying for existing settings, you will get an XML document similar to your request, except with all the values you requested filled in.

In a future post I'll walk through a example of creating a NetCF app that allows the user to write XML, send it to the device, and read the XML that comes back.  Subsequent posts will then describe various XML snippets that will accomplish some useful tasks.  If you want to see me cover a specific scenario and give example XML (since I personally find the MSDN docs for XML samples to be sparse at best) drop a comment below and I'll do the best I can to cover the topics.

Why .NET Compact Framework fails to call some HTTPS web servers
19 November 07 10:18 PM | andarno | 22 Comments   

A bug was discovered recently in the .NET Compact Framework version 2.0 (SP2 and earlier) and 3.5 that causes HttpWebRequest's directed at some HTTPS servers to fail with this error: (for web services)

System.Net.WebException: Unable to read data from the transport connection. ---> System.Net.Sockets.SocketException: Unknown error (0x0).
   at System.Net.HttpWebRequest.fillBuffer(HttpWebRequest request, Connection connection, CoreResponseData data)
   at System.Net.HttpWebRequest.getLine(HttpWebRequest request, Connection connection, CoreResponseData data)
   at System.Net.HttpWebRequest.parseResponse(HttpWebRequest request, Connection connection, Boolean defaultKeepAlive)
   at System.Net.HttpWebRequest.startReceiving(Connection connection)
   at System.Net.Connection.startReceiving(Object ignored)
   at System.Threading.ThreadPool.WorkItem.doWork(Object o)
   at System.Threading.Timer.ring()

   at System.Net.HttpWebRequest.finishGetResponse()
   at System.Net.HttpWebRequest.GetResponse()
   // ...
   at Program.Main(String[] args)

For standard HttpWebRequest calls from your app the stack trace would be different, although the exception message and cause would be the same.

While there are multiple reasons this exception can be produced, one thing that can cause it is if the server sends an empty encryption packet to the NetCF client.  Here is an illustration of the encryption process that occurs on the server for HTTPS:

First, a memory buffer is initialized with unencrypted data that the server wants to send to the client over the network.

Step 1:   unencrypted data  

Next, the server makes a call to EncryptMessage and the unencrypted data is encrypted in place.  A header and footer is added to the buffer:

Step 2: header encrypted data footer

This header if 5 bytes long, and the footer is several more bytes.  This entire packet, which is necessarily longer than the original unencrypted data, is then sent down the wire where the process is reversed using a call to DecryptMessage.

The trouble with NetCF's SSL stack is when the server encrypts a buffer of zero length and sends it to the client:

Step 1:   0 bytes of unencrypted data  
Step 2: header encrypted representation of a zero-length buffer footer

Although the original data had a length of zero, the encrypted version of it actually has a non-zero length.  When this packet is sent to a NetCF client, current versions of NetCF decrypt the packet and return a zero length buffer to the caller.  The semantics of a network Read method is traditionally that it blocks until some data is received, and if a zero length buffer is returned it is the sign that the socket was closed.  Because NetCF will return an empty buffer after decrypting an "empty" encryption packet, the caller may misinterpret this as a sign of a disconnected socket and terminate the connection.

In fact this is exactly what happens in NetCF's web services code when calling services over SSL that respond with empty encryption packets.  As a result the connection fails before the response is fully received and an exception is thrown.

What causes the server to send these empty encryption packets?  Technically it's legally allowed for these empty packets to be sent (although they are pointless), so depending on your server and its configuration this may never happen or it may happen regularly.

Unfortunately there is no way to get NetCF to ignore these packets, so the only workarounds are these, in order of simplicity:

  1. Don't use SSL to make your request (security implications here of course by sending your request and its response in clear text)
  2. Reconfigure your existing server in some way to get it to avoid generating these empty encryption packets.
  3. Build a new web server that will forward your device's requests on a separate connection to the ultimate target server and forward the responses back to the device.  This would work for web sites and web services.  But this new front-end server (which serves something of the role of a proxy server) would have to be configured to not generate these empty encryption packets.
  4. [Added 1/2/08] Build a device-side web proxy that calls into the SSPI functions itself (native would probably be easier than managed for this).  Then have your managed app call into the proxy.  This proxy would be responsible for consuming the empty packets and re-encrypting everything (if necessary to secure IPC) for the app on the same device without any empty SSL packets.
  5. Wait until a future version of NetCF that fixes this bug.
  6. Write your own HTTPS client using native code or P/Invoke'ing from NetCF (difficult to get right).

So how do you know for sure if the HTTPS server is sending empty encryption packets that are causing your NetCF clients to fail?  I wrote a native C++ app that calls a web server on an HTTPS connection and detects these packets.  (No, I'm afraid it can't filter them out for NetCF clients) By running this app against your web server the app will tell you whether your server generates these empty packets.  At least then you know what's going on.

[Updated Feb 1, 2008] You can download the source code from its resource page on the MSDN Code Gallery.

Filed under: , ,
Trying out the iPhone
07 November 07 08:00 AM | andarno | 1 Comments   

So I had the opportunity to try out the iPhone for a day.  Overall I was impressed, but there were some very basic features missing from many of the otherwise killer apps that I would greatly miss if I was to use the iPhone as my primary PDA and phone.  Here is a brief summary:

  Pros Cons
Browsing Beautiful. No copy and paste, so no convenient way to copy that phone number from a web page into the dialer.  No support for flash or any other 3rd party extensions.  No way to change the Google search default (that I could see).  Slow over Edge -- though much better over Wi-Fi.  I found I like to browse faster than the rendering engine can keep up, resulting in waiting while the page that has already downloaded just draws to the screen.
Notes Convenient, good auto-correct feature does a lot to make up for the non-tactile interface No undo.  And no select feature either.  If you want to delete more than a few characters, you'll hold down the delete key, end up deleting too much accidentally, and look for that missing Undo key, only to give up and accept data loss.
Email Nice to have a built-in app, especially when it connects to Gmail.  Good to have IMAP support to make up for the poor "Gmail"-dedicated option. When I added Gmail, all emails came in as Unread.  No bulk method to mark them as read either, so I would have had to open each of 47 messages to mark them as read.  Also no conversation view of email as Gmail on the web has -- just the archaic individual message listing.
Overall Stability The system was responsive most of the time. Sometimes the whole screen would just freeze for several seconds for no apparent reason.  It would not respond to the one button I could press or any touch on the screen.
Google Maps Multi-touch makes this great. I actually have found Microsoft Live Maps to provide a more accurate freeway traffic condition (don't know why), so I would prefer to have Live Maps offered.
Voicemail Of course I'm happy to see an app to visually make reviewing voicemails as easy as sifting an Inbox. No cons here -- except that I rarely have more than a single voicemail message waiting for me anyway, so I wouldn't call this a killer app.
YouTube Entertaining Poor video quality -- worse than a desktop computer it seemed.  Slow downloads over the Edge data connection.
Weather Convenient one or two touch access to weather daily forecasts. Unlike the dynamic calendar button on the home screen, the weather button reports a static "sunny 73 degree" all day every day.
Camera High quality.  Cute animated shutter. Issues pressing the "take picture" button not always taking the picture.  It could cause you to miss the perfect moment for your picture.
Phone dialer Nice interface.  I actually prefer the Windows Mobile Smartphone interface because it is more friendly to one-handed use.
Keyboard I personally really like the touchscreen keyboard, especially because it affords more room for the beautiful display.  Auto-correct makes up where mistakes occur. Virtually impossible to use with just one hand.  And yes, more mistakes are made than with a slide-out qwerty keyboard. 

Then of course there's the closed platform problem, and I miss the voice-activated search I have on my Windows Mobile Smartphone. 

Bottom line

Microsoft employment aside (since I tend to not let that get in the way of my personal purchase decisions), would I get an iPhone if I had $500 and a two-year contract I cared to get into?  I'm not sure.  If I did it would be for the mobile browsing experience and nothing more.  That's not worth $500 plus a two-year contract.  Check out a recent post I did on Windows Mobile Smartphones for how I like to find inexpensive, very usable smartphones.

Filed under:
More Posts Next page »

Search

Go

This Blog

Interesting blogs

Related sites

Syndication

Page view tracker