Category: NETCF

  • Contact.WebPage

    The Managed APIs in Windows Mobile 5.0 expose the Contact.WebPage as a Uri. The problem with this approach is that the user forms are free text and you can enter anything into this field. Probably 9 times out of 10 you’ll enter an address such as


    www.peterfoot.net


    And because the managed API passes this to the Uri constructor and it has no http:// prefix it fails and returns null. However there is a solution to workaround the issue. You can access the Properties collection and access the property and it will be returned as a string. So for example you would use:-


    [C#]
    string webPage = session.Contacts.Items[0].Properties[Microsoft.WindowsMobile.PocketOutlook.ContactProperty.WebPage];

    [VB]

    Dim webPage As String = session.Contacts.Items(0).Properties(Microsoft.WindowsMobile.PocketOutlook.ContactProperty.WebPage)


    If you then want to use a Uri with this value, check to see if it includes the http:// prefix and if not add your own e.g.


    Uri contactsWebpage = new Uri(“http://” + webPage);


     

  • OpenNETCF.Windows.Forms.Control2 in SDF 2.0

    In SDF v1 I wrote a class called ControlEx which would allow you to host a native windows control within a managed Control. This was the subject of an MSDN article (of which much of the concept applies here too). The implementation was rather convoluted to work around the limits of .NETCF v1.0, and the control was constructed with a Control, which hosted a MessageWindow with some of it’s style bits altered which in turn hosted the native control. Luckily hosting controls is easier in NETCF v2.0 but unlike the desktop this functionality is still not built into the base Control class. Which is where Control2 enters the stage.


    Like several other classes throughout the SDF the name has been changed to use the 2 suffix, but there are a few other changes I want to draw your attention to. First the inevitable disclaimer, what I’m going to describe won’t work with the current Beta release, as I was building the sample code for this post I realised there were a few “nice” features lacking to add a bit more reusable functionality into the Control2 class itself. So lets proceed with how the architecture has changed, and then work through specifics with a sample.


    The fundamental feature in .NETCF v2.0 which enables the new architecture is the ability to marshal a managed function as a function pointer to native code. Using this we are able to handle our wndproc in a managed method, this removes the need for the MessageWindow class. Control2 is derived from Control and when added to a form it hooks up a method to process its own windows messages. These are passed to a virtual WndProc method which can be overridden in a derived control to handle specific messages. After this is hooked up the CreateControl method is called, this takes the window class details returned from CreateParams and creates the window as a child of our Control2. This means than in it’s simplest form you only need to override CreateParams with a valid class name to host your control.


    However Control2 provides only basic functionality to interact with this class – for example automatically resizing when you resize the managed control, and getting/setting the control text. Beyond this you have two ways to interact with the control – add your own properties and methods to your Control2 derived class and from these send windows messages to the native control, and secondly add your own handlers for incoming messages from the control to create events.


    For a basic example I’ve wrapped the CAPEDIT control which is part of WindowsMobile – this is simply a textbox which can Automatically Capitalize The First Letter Of Every Word. Because CAPEDIT is a common control it requires a call to InitCommonControlsEx – there is a static helper method in Control2 to help with this:-


    Shared Sub New()


       Control2.InitCommonControls(&H2000)


    End Sub


    By the way as you’ve probably noticed, this and the rest of the example are in VB.NET, I would hope that it is self-explanitory enough for you to apply to C# as required.


    Interestingly we don’t need to worry about the constructor for the control at all, as we don’t do anything special. Where the magic happens is in our override for the CreateParams property:-


    Protected Overrides ReadOnly Property CreateParams() As OpenNETCF.Windows.Forms.CreateParams


       Get


          Dim cp As CreateParams = MyBase.CreateParams


          cp.ClassName = “CAPEDIT”


          Return cp


       End Get


    End Property


    And that’s it, all we did was change the ClassName in this case. The other parameters such as size are already set for you based on the current size of your managed control.


    So as I alluded to earlier, the example is a very simple class so there aren’t any events to show you, just a couple of properties which are implemented like so:-


    Private allcaps As Boolean = False


    Public Property UpCaseAllWords() As Boolean


       Get


          Return allcaps


       End Get


       Set(ByVal value As Boolean)


          Dim val As IntPtr


          If (value) Then


             val = New IntPtr(-1)


          End If


          Dim m As Microsoft.WindowsCE.Forms.Message = Microsoft.WindowsCE.Forms.Message.Create(Me.childHandle, 1025, val, IntPtr.Zero)


          Microsoft.WindowsCE.Forms.MessageWindow.SendMessage(m)


          allcaps = value


       End Set


    End Property


    The magic value of 1025 was determined from the commctrl.h header file. I should proobably have created it as a const with the same name as the native implementation. One of the great things about VS2005 is that when you build a project with such a control in it, you’ll get an automatic addition to your toolbar for it. We haven’t added any designer support (outside the scope of this article) but you can set descriptions, categories and the toolbox icon for the control in the usual way. By default you’ll get a rectangle with the name of the control which you can position on your form, in many cases this might be good enough. The sample project shows the control dropped on a form with a couple of checkboxes to toggle the properties. Also as a sanity check there is a button labelled Copy which copies the text value from the native control to a regular TextBox – remember we didn’t write any code to do that, it’s built into Control2 (well in a post-Beta release at least!). The standard behaviour of the CAPEDIT control is to have no border, but you can change this by setting the BorderStyle to BorderStyle.FixedSingle for a plain 1-pixel border.


    Control2Example.zip (9.07 KB)


    Wait what have I missed, oh yes there are two other useful properties – DesignMode allows you to check if your control is currently in a designer so you can for example not call device specific code, and ModifierKeys is a static property which gives you the current state of the Shift/Ctrl/Alt modifier keys, if your device has a “proper” keyboard.


    Stop Press:-


    Exhibit A – A CAPEDIT control in a managed app:-

  • OpenNETCF.IO in SDF 2.0

    As well as the revolutionary changes in the SDF v2.0, some of which are simply only made possible by improvements in .NETCF v2.0, we have used this opportunity to make a number of evolutionary changes too. For example I’m going to take a quick trip through the file functionality in the OpenNETCF.IO namespace:-


    DriveInfo – This is a new class which mimics functionality new in the desktop framework v2.0 and replaces previous separate classes for StorageCard and ObjectStore information. You can use it in two ways, use the constructor which takes a path and pass in the path of your storage device e.g. “” or “Storage Card” or you can use the static GetDrives() method which will return an array of all devices. A pleasant side-effect of this is that you can data-bind to this array of DriveInfo objects.


    OpenNETCF.IO.DriveInfo[] di = OpenNETCF.IO.DriveInfo.GetDrives();
    dataGrid1.DataSource = di;


    File2 – To follow accepted naming behaviour we have stopped using Ex as a name suffix and so instead our classes which overlap existing classes in .NETCF have the 2 suffix. I have added some additional helper methods which are found on the desktop to quickly read/write whole files – ReadAllText / ReadAllLines and WriteAllText / WriteAllLines.


    File2.WriteAllText(filename, textBox1.Text);


    FileSystemWatcher – We’ve made some improvements here so that all the various event combinations are correctly raised. Also we have built in designer support for VS2005 so you can drop a FileSystemWatcher onto your form and hook up the events in the designer.

  • GetDeviceUniqueID For VB

    To complement the C# version posted Here,  here is a working VB translation:-

    <System.Runtime.InteropServices.DllImport(“coredll.dll”)> _
    Private Shared Function GetDeviceUniqueID(ByVal appdata As Byte(), ByVal cbApplictionData As Integer, ByVal dwDeviceIDVersion As Integer, ByVal deviceIDOuput As Byte(), ByRef pcbDeviceIDOutput As Integer) As Integer
    End Function

    Private Function GetDeviceId(ByVal appData As String) As Byte()

    Dim appDataBytes As Byte() = System.Text.Encoding.ASCII.GetBytes(appData)
    Dim outputSize As Integer = 20
    Dim output(19) As Byte

    Dim result As Integer = GetDeviceUniqueID(appDataBytes, appDataBytes.Length, 1, output, outputSize)

    Return output

    End Function

  • Determine WM5.0 AKU Version Programmatically

    The full build and AKU version of a device is shown on the Start > Settings > System > About screen, however what if you want to determine the version from your code. Well starting with Windows Mobile 5.0 there is now a registry key which holds the AKU version e.g.


    RegistryKey kAku = Registry.LocalMachine.OpenSubKey(“SystemVersions”);

    string akuVersion = kAku.GetValue(“Aku”, “”);

    kAku.Close();


    This version will include a leading period so for example if the string is “.0.1.1” as on the current JasJar ROM the device has AKU 0.1, if it reads “.1.1.0” as on the current K-Jam ROM you have AKU 1.1. As far as I’m aware all shipping devices will have at least 0.1 as some fixes were implemented between RTM in May 2005 and the first shipping devices released later in the year. If your version is “.2.x.x” then you are a very lucky bunny indeed!


    Whilst Windows Mobile 2003 had a similar concept of periodic AKU updates throughout it’s lifetime this registry key is not available, nor is the AKU version displayed on the About dialog, only the Build number is displayed.

  • KeyboardPresent always returns false

    This bug affects both managed and native developers working with the Windows Mobile 5.0 SDK.


    Native


    Due to an error in snapi.h the location of the registry key used to indicate if a hardware keyboard is present is incorrect:-


    ////////////////////////////////////////////////////////////////////////////////
    // KeyboardPresent
    // Gets a value indicating whether a keyboard is attached and enabled.
    #define SN_KEYBOARDPRESENT_ROOT HKEY_LOCAL_MACHINE
    #define SN_KEYBOARDPRESENT_PATH TEXT(“SoftwareMicrosoftShell”)
    #define SN_KEYBOARDPRESENT_VALUE TEXT(“HasKeyboard”)


    The root key for this value should instead be HKEY_CURRENT_USER. Once you use this location you can correctly determine if there is a hardware keyboard (this is the same location as used on WM2003 Second Edition devices such as the HTC Blue Angel).


    Managed


    The SystemState class also uses the incorrect location to read this value so the property will always return false. The workaround here is to access the registry directly using either the Microsoft.Win32.RegistryKey in .NETCF v2.0 e.g.


    RegistryKey keyboardKey = Registry.CurrentUser.OpenSubKey(“SoftwareMicrosoftShell”);

    int dwHasKeyboard = keyboardKey.GetValue(“HasKeyboard”, 0);

    keyboardKey.Close();

    bool hasKeyboard = (dwHasKeyboard != 0);

  • Bug in Microsoft.WindowsMobile.Telephone.Phone.Talk

    Luis Cabrera has posted the details of a bug identified in the Talk method to the Windows Mobile Team blog. The workaround (see the original post) is to append a null character to the end of your dial string.


    Note: this bug doesn’t affect InTheHand.WindowsMobile.Telephony.Phone.Talk.

  • PocketOutlook Native Managed Map

    Windows Mobile 5.0 introduces a managed API which wraps both POOM and a subset of CEMAPI (enough to send an Email / Sms). The table below is designed to show how the managed objects map to the interfaces which will be familiar to seasoned POOM developers. It also shows those parts of POOM which are not available through the managed APIs:-

















































































































































































































    Native


    Managed


    IPOutlookApp


    OutlookSession


    · Logon


    Automatically handled


    · Logoff


    Automatic – call Dispose on your OutlookSession when finished


    · get_Version


    Not Supported – Get OS version with System.Environment.OSVersion.Version


    · GetDefaultFolder


    Use strongly typed folder properties – Appointments, Contacts & Tasks


    Infrared not supported


    · CreateItem


    Use default constructor for specific item type


    · GetItemFromOid


    Use constructor for item which accepts an ItemId e.g.


    new Contact(new ItemId(oid)


    · City Methods (Removed in Pocket PC 2002)


    Obsolete


    · ReceiveFromInfrared (Obsolete from Pocket PC 2002)


    Obsolete


    · get_OutlookCompatible


    Obsolete


    · GetTimeZoneFromIndex


    Not Supported


    IFolder


    Folder – AppointmentFolder, ContactFolder, TaskFolder


    Infrared not supported


    · get_Items


    Items


    · get_DefaultItemType


    N/A inferred from the Folder type


    · get_Application


    N/A


    · AddItemToInfraredFolder


    Not Supported


    · SendToInfrared


    Not Supported


    · ReceiveFromInfrared


    Not Supported


    IPOutlookItemCollection


    PimItemCollection – AppointmentCollection, ContactCollection, TaskCollection


    · Add


    Add


    · get_Count


    Count


    · Find


    Find (Requires a PropertyDescriptor)


    · FindNext


    Not Supported


    · Item


    · 1-based index


    Default indexer – () VB or [] C#


    0-based index


    · Remove


    RemoveAt – also implements Remove(PimItem)


    · Restrict


    Restrict


    · Sort


    Sort


    · get_IncludeRecurrences


    Not Supported


    · put_IncludeRecurrences


    Not Supported


    IAppointment


    Appointment


    · ClearRecurrencePattern


    Call Clear on the AppointmentRecurrence


    · GetRecurrencePattern


    RecurrencePattern


    · get_* / put_*


    Accessible by named properties or via Ids through the Properties collection


    · Save


    Update


    · Send


    Send


    · Delete


    Delete


    · Cancel


    Cancel


    · Copy


    Copy


    · Display


    ShowDialog


    · get_Oid


    ItemId


    · get_BodyInk / put_BodyInk


    BodyInk no longer supported on WM5.0


    IContact


    Contact


    · get_* / put_*


    Accessible by named properties or via Ids through the Properties collection


    · Save


    Update


    · Delete


    Delete


    · Copy


    Copy


    · Display


    ShowDialog


    · get_Oid


    ItemId


    · get_BodyInk / put_BodyInk


    BodyInk no longer supported on WM5.0


    ITask


    Task


    · ClearRecurrencePattern


    Call Clear on the TaskRecurrence


    · GetRecurrencePattern


    RecurrencePattern


    · get_* / put_*


    Accessible by named properties or via Ids through the Properties collection


    · Save


    Update


    · Delete


    Delete


    · SkipRecurrence


    Call Skip on the TaskRecurrence


    · Copy


    Copy


    · Display


    ShowDialog


    · get_Oid


    ItemId


    · get_BodyInk / put_BodyInk


    BodyInk no longer supported on WM5.0


    IItem


    New in WM5.0 supports access to custom properties etc Functionality is available via Appointment, Contact and Task types


    IRecipients


    RecipientCollection


    Shared with the Email (MAPI) support


    IRecipient


    Recipient


    Shared with the Email (MAPI) support


    IRecurrencePattern


    Recurrence – AppointmentRecurrence, TaskRecurrence


    Properties follow same names as native equivalent. Managed types such as TimeSpan and DateTime are used as appropriate. Enums defined for Months, WeekOfMonth, DaysOfWeek


    IExceptions


    Not supported


    IException


    Not supported


    ITimeZone


    Not supported

  • XmlSerialization of DateTime in .NETCF 2.0

    Changes have been made to the DateTime type in v2.0 to help indicate whether a specific value represents a local time or universal time. What this can mean is that the behaviour of web services using DateTimes will change. This article has full details of the issue:-


    http://blogs.msdn.com/mattavis/archive/2005/10/11/479782.aspx


    The same approach is available under .NETCF v2.0 to request the old v1 behaviour – add a configuration file for your exe with the following content:-


    <?xml version=”1.0″ encoding=”utf-8″ ?>


    <configuration>


       <system.xml.serialization>


          <dateTimeSerialization mode=”Local” />


       </system.xml.serialization>


    </configuration>

  • Issue with Microsoft.WindowsMobile.PocketOutlook.RecipientCollection.Add()

    When you want to create a meeting request with managed POOM on WM5.0 you start by creating an Appointment and then add Recipient objects to it’s Recipients collection. However what the documentation doesn’t tell you is that you have to ensure that your Recipient has both the Name and Address properties. For example:-


    Microsoft.WindowsMobile.PocketOutlook.Appointment ma = new Microsoft.WindowsMobile.PocketOutlook.Appointment();
    ma.Subject = “Test 5”;
    ma.Recipients.Add(new Microsoft.WindowsMobile.PocketOutlook.Recipient(“user@yourdomain.com));


    Will throw a Win32Exception with the text “Native method call failed”. This isn’t very descriptive but the reason it fails is due to how native POOM works – the IRecipients.Add method takes a name argument and since your Name property is empty this call fails. If you specify the name e.g.


    ma.Recipients.Add(new Microsoft.WindowsMobile.PocketOutlook.Recipient(“User”,”user@yourdomain.com));


    or even this if you only have an address:-


    ma.Recipients.Add(new Microsoft.WindowsMobile.PocketOutlook.Recipient(user@yourdomain.com,”user@yourdomain.com));


    Then you’ll be okay.