# Tuesday, November 09, 2004

Hosting a Native Windows Control - Live on MSDN

My latest .NETCF article is now live in the MSDN library. This is an update of the control hosting article previously published here at OpenNETCF.org. The process has been simplified so that there is now a standard base class (ControlEx) which does most of the hard work for you, the process of hosting a control is therefore much closer to the desktop experience. The article contains a sample using the WebBrowser control, which shows not only setting properties and calling methods for the control, but also reacting to notifications from the native control.

#    Comments [1] |
# Thursday, November 04, 2004

Virtual Bob

Virtual PC is a great tool when testing and developing as you run multiple OS versions and configurations on a single machine. However Ben Armstrong has found a more unusual use for Virtual PC - to run Microsoft Bob, the short-lived home oriented shell around windows 3.11 / 95. See the full story including nostalgic screen-shots here on Ben's blog.

#    Comments [0] |
# Tuesday, November 02, 2004

Open All Hours

Sometimes you want to ensure that the system will not automatically close down your application when it goes into the background. You can achieve this by handling the Closing event of your main form, the event arguments passed to your handler will allow you to Cancel the close. Therefore you can set a boolean member to ensure that the operation only succeeds once you decide it's okay to close:-

bool keepopen = true;

private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)

{

   if(keepopen)

   {

      e.Cancel = true;

   }

}

 

Then based on some pre-determined action in your program you can set keepopen to false and the form will close down.

#    Comments [0] |

The Naked Emulator

If you want to display the standard Pocket PC emulator without the regular skin, so just the screen itself is shown you can modify a couple of the settings files and skin bitmaps. For the default Pocket PC 2002 emulator you'll find the files here by default:-

C:\Program Files\Microsoft Visual Studio .NET 2003\CompactFrameworkSDK\ConnectionManager\Bin\Images\PocketPC\2002\1033

Make a backup before you alter anything!

Then replace the ppc2002.xml file with this one. Finally add this image to the folder which is the null skin, necessary in order that the emulator window is sized correctly. The finished result should look like this:-

 

Naked Emulator
#    Comments [0] |
# Friday, October 29, 2004

Great Installation advice on the Smartphone Developer newsgroup

Lars Peter recently posted this excellent post on the microsoft.public.smartphone.developer newsgroup.

In the post Lars describes using Nullsoft Installer (NSIS) to build a desktop installation package to include multiple cab file installations. This is ideal for those scenarios when you want to deploy more than just one application cab file, for example OpenNETCF's Smart Device Framework, Sql Ce or the .NETCF runtimes themselves.

Lars' post includes a sample install script, which you can pass a number of cab files for installation. You can also download the sample script from Lars' server.

Nice one Lars!

#    Comments [0] |
# Wednesday, October 20, 2004

Orange SPV C500 ROM Update - Go get it!

Spotted over at MoDaCo, Orange have released an updated ROM which fixes the GAPI issues on the device and a few other fixes:-

  • Videos can be sent via MMS
  • Flickering issue in games has now been fixed
  • Packet video full screen mode and backlight fix
  • Improved memory management of the camera and video applications
  • Improved browsing/upload speed (changed from GPRS class 8 to class 10)

It's a 22mb download from Orange's website

#    Comments [0] |

Need a GUID in a hurry?

There may be times in your application you need to generate a new unique Guid. The System.Guid class in .NETCF v1.0 doesn't have the NewGuid method which is what you would normally use on the desktop. There are a couple of proposed alternatives, either generating one yourself by following the standards for Guids - using a few P/Invokes to Crypto API methods to get random numbers, using the GuidEx class which uses the same technique or indirectly using SqlCe to create a new identity value.

An easier way in many cases is to use one of the COM subsystem API methods as this involves only a single P/Invoke call. The only caveat to this is that not all CE based systems have full COM support - there are three varieties Minimal COM, Full COM and DCOM support. Minimal COM doesn't support Guid generation. However in my brief experimentation with Platform Builder it would appear that among .NETCF's prerequisites is COM support so this should in theory be supported by any CE device on which .NETCF is supported. It is certainly supported on all Windows Mobile devices:-

namespace InTheHand

{

      ///

      /// Helper class for generating a globally unique identifier (GUID).

      ///

      /// "System.Guid"/>

      public sealed class ComGuid

      {

            private ComGuid(){}

 

            ///

            /// Initializes a new instance of the "System.Guid"/> class.

            ///

            /// A new "System.Guid"/> object

            public static Guid NewGuid()

            {

                  Guid val = Guid.Empty;

 

                  int hresult = 0;

 

                  hresult = CoCreateGuid(ref val);

 

                  if(hresult != 0)

                  {

                        throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error(), "Error creating new Guid");

                  }

 

                  return val;

            }

 

            [DllImport("ole32.dll", SetLastError=true)]

            private static extern int CoCreateGuid(ref Guid pguid );

      }

}

 

#    Comments [0] |
# Thursday, October 14, 2004

Create a Top-Most form

The following code can be used to force your form to the top of the z-order. Use this functionality with care since it's bad practice to hog the topmost position which could obscure other important functionality.

private void Form1_Load(object sender, System.EventArgs e)

{

      //get handle

      this.Capture = true;

      IntPtr hwnd = OpenNETCF.Win32.Win32Window.GetCapture();

      this.Capture = false;

 

      //set foreground

      OpenNETCF.Win32.Win32Window.SetWindowPos(hwnd, Win32Window.SetWindowPosZOrder.HWND_TOPMOST, 0, 0, 0, 0, Win32Window.SetWindowPosFlags.SWP_NOMOVE | Win32Window.SetWindowPosFlags.SWP_NOSIZE | Win32Window.SetWindowPosFlags.SWP_SHOWWINDOW);

 

}

Note that this code cannot be placed in the form constructor, since the native form has yet to be created so a valid window handle won't be returned. Here the code is placed into the Form_Load method.

In .NETCF 2.0 you can set the TopMost property of your form which removes the need for any of this interop (Thanks Daniel Moth for correcting this).

#    Comments [1] |
# Wednesday, October 13, 2004

.NETCF Chat Tomorrow

As part of a regular series of .NETCF technical chats there will be an MVP hosted chat tomorrow (14th October) on any aspect of .NET Compact Framework and Smart Device programming. 10-11am PDT, 17-18 GMT

Add a reminder to your calendar

More details, and details of other technical chats, here at MSDN.

#    Comments [0] |
# Saturday, October 09, 2004

Bring a .NETCF Form to the foreground

Sometimes calling BringToFront for your form is not enough to bring your app to the foreground. You can P/Invoke SetForegroundWindow which will activate your app and bring the window to the front of the Z-Order:-

C#

public void SetForegroundWindow()
{
    this.Capture = true;
    IntPtr hwnd = OpenNETCF.Win32.Win32Window.GetCapture();
    this.Capture = false;
    OpenNETCF.Win32.Win32Window.SetForegroundWindow(hwnd);
}

or VB

Public Sub SetForegroundWindow
    Me.Capture = True
    Dim hwnd As IntPtr = OpenNETCF.Win32.Win32Window.GetCapture()
    Me.Capture = False
    OpenNETCF.Win32.Win32Window.SetForegroundWindow(hwnd)
End Sub

#    Comments [0] |
# Thursday, October 07, 2004

Fixing ComboBox bugs...

The standard ComboBox in NETCF has a couple of "issues", however it's possible to workaround them with a bit of tweaking. I rolled together a number of these fixes into a ComboBoxEx class. Heres the code (C#), I hope this (with a few more missing features) will be in the next SDF build with designer support like our other controls:-

namespace OpenNETCF.Windows.Forms

{

      /// <summary>

      /// Extended ComboBox control.

      /// </summary>

      public class ComboBoxEx : ComboBox, IWin32Window

      {

            //windows messages

            private const int WM_SETREDRAW = 0x0b;

            private const int CB_SETCURSEL = 0x014E;

            private const int CB_DELETESTRING = 0x0144;

            private const int CB_INSERTSTRING = 0x014A;

 

            //native window handle

            private IntPtr m_handle;

 

            //databound collection

            private IBindingList thebindinglist = null;

 

            //is display updatable?

            private bool m_updatable = true;

 

            /// <summary>

            /// Gets the window handle that the control is bound to.

            /// </summary>

            public IntPtr Handle

            {

                  get

                  {

                        if(m_handle == IntPtr.Zero)

                        {

                              this.Capture = true;

                              m_handle = Win32Window.GetCapture();

                              this.Capture = false;

                        }

 

                        return m_handle;

                  }

            }

 

            // Redraw code courtesy of Alex Feinman - http://blog.opennetcf.org/afeinman/PermaLink,guid,9305a1d9-e24e-4310-89e2-f80808076a37.aspx

 

            /// <summary>

            /// Maintains performance when items are added to the <see cref="ComboBoxEx"/> one at a time.

            /// </summary>

            public void BeginUpdate()

            {

                  m_updatable = false;

 

                  Win32Window.SendMessage(this.Handle, WM_SETREDRAW, 0, 0);

            }

 

            /// <summary>

            /// Resumes painting the <see cref="ComboBoxEx"/> control after painting is suspended by the <see cref="BeginUpdate"/> method.

            /// </summary>

            public void EndUpdate()

            {

                  m_updatable = true;

                  Win32Window.SendMessage(this.Handle, WM_SETREDRAW, 1, 0);

            }

 

            //ComboBox doesn't support ItemChanges in a datasource implementing IBindingList

            //The following workaround forces the list to update if an item is changed

 

            //data source has changed

            protected override void OnDataSourceChanged(EventArgs e)

            {

                  //remove event handler

                  if(thebindinglist != null)

                  {

                        thebindinglist.ListChanged-= new ListChangedEventHandler(ComboBoxEx_ListChanged);

                        //reset our handle to the bound data

                        thebindinglist = null;

                  }

 

                  //get the underlying ibindinglist (if there is one)

                  if(this.DataSource is IListSource)

                  {

                        IList thelist = ((IListSource)this.DataSource).GetList();

                        if(thelist is IBindingList)

                        {

                              thebindinglist = (IBindingList)thelist;

                        }

                  }

                  else if(this.DataSource is IBindingList)

                  {

                        thebindinglist = (IBindingList)this.DataSource;

                  }

                 

                  if(thebindinglist != null)

                  {

                        //hook up event for data changed

                        thebindinglist.ListChanged+=new ListChangedEventHandler(ComboBoxEx_ListChanged);

                  }

                 

                  base.OnDataSourceChanged (e);

            }

 

            //called when a change occurs in the bound collection

            private void ComboBoxEx_ListChanged(object sender, ListChangedEventArgs e)

            {

                  if(m_updatable)

                  {

                        if (e.ListChangedType == ListChangedType.ItemChanged)

                        {

                              //update the item

 

                              //delete old item

                              Win32Window.SendMessage(this.Handle, CB_DELETESTRING, e.NewIndex, 0);

                              //get display text for new item

                              string newval = this.GetItemText(this.Items[e.NewIndex]);

                              //marshal to native memory

                              IntPtr pString = MarshalEx.StringToHGlobalUni(newval);

                              //send message to native control

                              Win32Window.SendMessage(this.Handle, CB_INSERTSTRING, e.NewIndex, pString);

                              //free native memory

                              MarshalEx.FreeHGlobal(pString);

                        }

                  }

            }

 

            /// <summary>

            /// Get or Set the selected index in collection.

            /// </summary>

            /// <remarks>Overridden to overcome issue with setting value to -1 (http://support.microsoft.com/default.aspx?scid=kb;en-us;327244)</remarks>

            public override int SelectedIndex

            {

                  get

                  {

                        return base.SelectedIndex;

                  }

                  set

                  {

                        if(value == -1)

                        {

                              Win32Window.SendMessage(this.Handle, CB_SETCURSEL, -1, 0);

                        }

                        else

                        {

                              base.SelectedIndex = value;

                        }

                  }

            }

 

      }

}

 

So whats in the above code? Well it integrates Alex Feinman's recent tip to implement Begin/EndUpdate methods to suspend redrawing when populating the control.

Secondly if works around a bug in the DataBinding support for ComboBox. If you make an edit to an existing item in the bound collection the combo wont normally update (It does correctly react to additions and deletions).

Thirdly it overcomes a known issue with resetting the selected index property (by assigning -1) which normally has to be done twice to take effect - details in this KB article - Thanks to Tim Wilson for posting this KB on the newsgroup.

As a Bonus I've added a Handle property so you can get at the native control handle - which in conjunction with Win32Window methods allows you to tweak other aspects of the control.

UPDATE: Changed the databinding code to update a single item via P/Invoke rather than refreshing the list. Changed the SelectedIndex to set via P/Invoke - avoids raising the SelectedIndexChanged event.

#    Comments [0] |
# Wednesday, October 06, 2004

Are you a UK Smartphone User or Developer?

If so then you'll be interested to know that MoDaCo will be hosting a Smartphone event in Birmingham on Saturday 6th November. This will have both developer and non-developer content running in parallel. Full details with venue and sign-up details here. It looks like it'll be a very interesting event for all and on a much bigger scale than previous MoDaCo events.

If you are a developer Monolithix over at MoDaCo would like suggestions for developer topics to be covered at the event. If you do, reply to this thread.

#    Comments [0] |
# Tuesday, October 05, 2004

Blogging from beside the seaside!

Thanks to an administrative blunder in a European Union statistics report, I've found out that I actually live nearer the seaside than I thought, it seems Wales has disappeared :-)

 

Eurostat yearbook 2004

More details on the BBC News site

#    Comments [1] |
# Saturday, October 02, 2004

Some new OpenNETCF code

I've uploaded to our online source browser some of the new code which will feature in the next Smart Device Framework release. This includes a new library for WindowsCE specific functionality, designed to match the new functionality available in the Microsoft.WindowsCE.Forms assembly in the .NET Compact Framework v2.0 which is currently in Beta with Visual Studio 2005.

For Smartphone developers this includes the InputModeEditor which allows you to set a specific input mode for an edit control. Therefore when the control receives focus, the required input mode is automatically selected for the user - useful for numeric only fields or a field where you want to capture some form of text but know that T9 would be inappropriate.

For Pocket PC 2003 Second Edition the SystemSettings class allows you to easily change the screen orientation with a single statement:-

SystemSettings.ScreenOrientation = ScreenOrientation.Angle90;

There are also some changes to existing classes - specifically the RegistryKey has had a number of useful changes:-

  • ToString now matches the behaviour on the full .NET Framework (In the current release there is a subtle difference in the format of the string returned)
  • CreateSubKey allows you to create volatile registry keys on supported Windows CE 5.0 systems.
  • GetValue now correctly returns DWORD values as an Int32 rather than UInt32, this is now consistent with the desktop and makes life a lot easier.
  • We have a full implementation of GetValueKind and the RegistryValueKind enumeration - this functionality is part of the full .NET Framework v2.0 Beta but we are making it available now for .NETCF v1.0

All of this code is currently available in the source tree which you can browse online. Object model documentation has also been updated in the online library - which now exceeds 6000 individual pages. Expect to see all this code built into the next binary release.

#    Comments [0] |
# Friday, September 17, 2004

New Visual Studio 2005 edition announced

One of the stumbling blocks to getting into .NETCF development today is that the only supported tool for development is Visual Studio 2003 Professional or higher.

Microsoft announced today at VSLive a new addition to the Visual Studio family in the 2005 version - Visual Studio Standard Edition. This will be a significantly cheaper version of Visual Studio but will include the full device development experience with both VB.NET and C# for managed code and C++ for native code.

A more detailed comparison of the editions is available here:-

http://lab.msdn.microsoft.com/vs2005/productinfo/productline/default.aspx

The full press release can be viewed here:-

http://www.microsoft.com/presspass/press/2004/sep04/09-13VSLiveOrlandoPR.asp

#    Comments [0] |
# Thursday, September 09, 2004

Keep your Smartphone backlight on

If your application involves displaying screen content you probably have come across the issue where the screen backlight turns off after a few seconds of no keypresses. You can override this behaviour in your application using a couple of underdocumented API Power-Management functions. Here is a VB.NET snippet for .NETCF to keep the backlight on:-

Namespace OpenNETCF.WindowsCE.Forms

 

Public Class Backlight

 

'ensure the power requirement is released

Protected Overrides Sub Finalize()

Release()

End Sub

'handle to the power requirement

Private handle As IntPtr

 

Private Enum PowerState

PwrDeviceUnspecified = -1

'full on

D0 = 0

'low power

D1 = 1

'standby

D2 = 2

'sleep

D3 = 3

'off

D4 = 4

PwrDeviceMaximum = 5

End Enum

 

'keep the backlight lit

Public Sub Activate()

'request full power

handle = SetPowerRequirement("BKL1:", PowerState.D0, 1, IntPtr.Zero, 0)

End Sub

 

'release power requirement

Public Sub Release()

If handle.ToInt32() <> 0 Then

Dim result As Integer

result = ReleasePowerRequirement(handle)

handle = IntPtr.Zero

End If

End Sub

Private Declare Function SetPowerRequirement Lib "coredll.dll" (ByVal pvDevice As String, ByVal DeviceState As PowerState, ByVal DeviceFlags As Integer, ByVal pvSystemState As IntPtr, ByVal StateFlags As Integer) As IntPtr

Private Declare Function ReleasePowerRequirement Lib "coredll.dll" (ByVal handle As IntPtr) As Integer

End Class

End Namespace

Thanks to Paul O'Brien from MoDaCo for inspiring and helping test the code.

#    Comments [0] |

New tool for Visual Studio help integration

Creating help files which seamlessly integrate with Visual Studio 2003 is a pain. Microsoft have released a Help Integration Wizard (Beta). This is designed to walk through the process of creating a setup which will integrate a HTML Help 2.0 file with Visual Studio.

Currently to integrate your own help you either have to get down and dirty and edit the tables of data within an .MSI installer, or use a third-party tool (H2Reg) to register the help collection without using an MSI installer. The first method is documented with the Visual Studio Help Integration Kit but is by no means clear, and if you start making changes to your .MSI project you generally have to start all over again.

I've downloaded and begun to test the wizard and it looks promising, however it appears to have a few issues with the help files generated by the latest version of NDoc (1.3b1a). Hopefully this can be overcome either by tweaking the options in NDoc or by some manual editing of the generated files. When I find a solution I'll post again here.

Read about the tool (and download the beta) here:-

http://msdn.microsoft.com/vstudio/default.aspx?pull=/library/en-us/dv_vstechart/html/integration_wizard.asp

 

[Update - It appears it's not particularly new, just well hidden! ]