Friday, March 03, 2006

One of my colleagues recently purchased the Treo 700w and fall in love with it. Being a developer he came up with the idea to try to use the button on the phone's headphones to initiate the Voice Commander. So he approached me with the question on how to place a hook in the WM device in order to listen for all hardware button presses. As a matter of fact, Windows CE does support a keyboard hook through the SetWindowsHookEx API. This API requires the usage of the native callback, which was not possible in the first version of the CF. The current version of .NetCF empowered us with this very useful capability. So what are hooks? Put shortly, a hook is a function that you can create as a part of your application in order to monitor on the messages inside the operating system. Hooks were provided by Microsoft primarily to help developers with the debugging their applications. The incorrect usage of them very easily could bring the system down. Therefore the hooks should be a last resort when all other a more traditional methods don't satisfy your requirents. Below you will find the class HookKeys that wraps all appropriate API calls and exposes all catched messages as a managed HookEvent:

public class HookKeys

{

        #region delegates

 

        public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);

        public delegate void HookEventHandler(HookEventArgs e, KeyBoardInfo keyBoardInfo);

 

        public HookEventHandler HookEvent;

 

        #endregion

 

        #region fields

 

        private HookProc hookDeleg;

        private static int hHook = 0;

 

        #endregion

 

 

        public HookKeys()

        {

 

        }

 

        #region public methods

        ///

        /// Starts the hook

        ///

        public void Start()

        {

            if (hHook != 0)

            {

                //Unhook the previouse one

                this.Stop();

            }

 

            hookDeleg = new HookProc(HookProcedure);

            hHook = SetWindowsHookEx(WH_KEYBOARD_LL, hookDeleg, GetModuleHandle(null), 0);

 

            if (hHook == 0)

            {

                throw new SystemException("Failed acquiring of the hook.");

            }

        }

 

        ///

        /// Stops the hook

        ///

        public void Stop()

        {

            UnhookWindowsHookEx(hHook);

        }

 

        #endregion

 

        #region protected and private methods

 

        protected virtual void OnHookEvent(HookEventArgs hookArgs, KeyBoardInfo keyBoardInfo)

        {

            if (HookEvent != null)

            {

                HookEvent(hookArgs, keyBoardInfo);

            }

        }

     

 

        private int HookProcedure(int code, IntPtr wParam, IntPtr lParam)

        {

           KBDLLHOOKSTRUCT hookStruct =  (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));

 

           if (code < 0)

                return CallNextHookEx(hookDeleg, code, wParam, lParam);

 

           // Let clients determine what to do

           HookEventArgs e = new HookEventArgs();

           e.Code = code;

           e.wParam = wParam;

           e.lParam = lParam;

 

           KeyBoardInfo keyInfo = new KeyBoardInfo();

           keyInfo.vkCode = hookStruct.vkCode;

           keyInfo.scanCode = hookStruct.scanCode;

           OnHookEvent(e, keyInfo);

 

           // Yield to the next hook in the chain

           return CallNextHookEx(hookDeleg, code, wParam, lParam);

 

       }

 

        #endregion

 

       #region P/Invoke declarations

 

       [DllImport("coredll.dll")]

       private static extern int SetWindowsHookEx(int type, HookProc hookProc, IntPtr hInstance, int m);

 

       [DllImport("coredll.dll")]

       private static extern IntPtr GetModuleHandle(string mod);

 

       [DllImport("coredll.dll")]

       private static extern int CallNextHookEx(

               HookProc hhk,

               int nCode,

               IntPtr wParam,

               IntPtr lParam

               );

 

       [DllImport("coredll.dll")]

       private static extern int GetCurrentThreadId();

 

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

       private static extern int UnhookWindowsHookEx(int idHook);

 

       private struct KBDLLHOOKSTRUCT

       {

           public int vkCode;

           public int scanCode;

           public int flags;

           public int time;

           public IntPtr dwExtraInfo;

       }

 

       const int WH_KEYBOARD_LL = 20;

 

       #endregion

   }

 

        #region event arguments

  

    public class HookEventArgs : EventArgs

    {

        public int Code;    // Hook code

        public IntPtr wParam;   // WPARAM argument

        public IntPtr lParam;   // LPARAM argument

    }

 

    public class KeyBoardInfo

    {

        public int vkCode;

        public int scanCode;

        public int flags;

        public int time;

    }

 

        #endregion

 

 

In order to use this class in your program, just declare the varialble and hook up into HookEvent:

 

HookKeys hook = new HookKeys();

hook.HookEvent += new HookKeys.HookEventHandler(HookEvent);  

The HookKeys class should work properly, but there is something missing from this class to be complete. I am not going to tell you what it is. This is going to be a self learning quiz for my readers. You can post the anwers in the comments. I'll anounce the winners in the next post.

3/3/2006 10:32:08 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, January 17, 2006

The DateTimePicker control in the .NetCF v2 doesn't implement DropDown and CloseUp events which could be really helpful whe dealing with the picker. It doesn't mean that these events or messages are not available in the native counterpart. Win CE SDK docs show the presence of the DTN_CLOSEUP and DTN_DROPDOWN notification messages. It means that they are sent by the DateTime picker to its parent. So, I pulled the NativeWindow class to whip up the DateTimePickerEntender class that suclasses the form, catches the messages and raises the events to the client:

public class DateTimePickerExtender : NativeWindow

{

        public event System.EventHandler CloseUp;

        public event System.EventHandler DropDown;

 

        private IntPtr handle;

 

        public DateTimePickerExtender(IntPtr handle)

        {

            this.handle = handle;

            // Subclass the window

            this.AssignHandle(handle);

        }

 

        protected override void WndProc(ref Message m)

        {

            // Check for notification messages

            if (m.Msg == WM_NOTIFY)

            {

                NMHDR nmhdr = new NMHDR();

                        Marshal.PtrToStructure(m.LParam, nmhdr);

                switch (nmhdr.code)

                {

                    case DTN_DROPDOWN:       

                        // Raise the event

                        if (DropDown != null)

                            DropDown(this, new EventArgs());

                        break;

                    case DTN_CLOSEUP:

                        // Raise the event

                        if (CloseUp != null)

                            CloseUp(this, new EventArgs());

                        break;

                }

            }

            base.WndProc(ref m);

        }

 

 

        #region P/Invoke declarations

 

        private const int WM_NOTIFY = 0x004E;

 

        private const int DTN_FIRST = -760;

        private const int DTN_DROPDOWN = (DTN_FIRST + 6);

        private const int DTN_CLOSEUP = (DTN_FIRST + 7);

 

        private class NMHDR

        {

            public IntPtr hwndFrom = IntPtr.Zero;

            public uint idFrom = 0;

            public int code = 0; //uint

        }

 

        #endregion

}

Drop the DateTimePicker controls on the form, create and instance of the DateTimePickerExtender and hook up into the events:

DateTimePickerExtender dtEntender;

 

public Form1()

{

     InitializeComponent();

     dtEntender = new DateTimePickerExtender(this.Handle);

     dtEntender.CloseUp += new EventHandler(dtEntender_CloseUp);

     dtEntender.DropDown += new EventHandler(dtEntender_DropDown);

}

 

void dtEntender_DropDown(object sender, EventArgs e)

{

     Console.WriteLine("DropDown event");

}

 

void dtEntender_CloseUp(object sender, EventArgs e)

{

     Console.WriteLine("CloseUp event");

}

 

1/17/2006 7:35:53 PM (GMT Standard Time, UTC+00:00)  #     | 
 Thursday, January 05, 2006

Questions about embedding Macromedia Flash in CF application pop up from time to time on the CF NG. In fact it is very easy to do with Flash ActiveX for Pocket PC and WebBrowser control.

Here are five steps to achieve that:

1. Download and install Flash ActiveX for Pocket PC on your Pocket PC.

2. Create Smart Device and CF v2 project in VS 2005 and drop WebBrowser on your form.

3. Add flash file (.SWF) of your choice to your project and set its Built Action to content and “Copy if newer”

4. Create html file and make sure that you have a full path to the .SWF file in the src tag value:

<html>

<head>

<title>Dogshow Gametitle>

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

head>

 

<body bgcolor="#FFFFFF" margintop=0 topmargin=0 leftmargin=0 marginleft=0>

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=5,0,0,0" width="580" height="360">

  <param name=movie value="dogshow.swf">

  <param name=quality value=high>

  <embed src="file://\Program Files\MMediaClient\dogshow.swf" quality=high pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash" type="application/x-shockwave-flash" width="580" height="360">

  embed>

 

object>

body>

html>

5. Make a call to the Navigate method with the name of your html file:

webBrowser1.Navigate(new Uri(@“file://\Program Files\MMediaClient\dogshow.htm“));

 

And enjoy the show:

1/5/2006 4:56:57 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, January 03, 2006

You probably know how difficult it was to create a screen snapshot in CF v1. It required usage of many API calls and custom marshaling of a few structures. Just take a look at these resources:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/scibf.asp?frame=true

http://vault.netcf.tv/VaultService/VaultWeb/Blame.aspx?repid=2&path=%24%2fSDF%2fv1.4%2fOpenNETCF.Drawing%2fGDIPlus.cs&version=5&includedversions=20

The world has become a much better place since release of the CF v2 - availability of the native handles and ability to save image from Bitmap - have reduced the required code to just a few lines and one API call:

private void Snapshot(string filename, Graphics gx, Rectangle rect)

{

     Bitmap bmp = new Bitmap(rect.Width, rect.Height);

     // Create compatible graphics

     Graphics gxComp = Graphics.FromImage(bmp);

     // Blit the image data

     BitBlt(gxComp.GetHdc(), 0, 0, rect.Width, rect.Height, gx.GetHdc(), rect.Left, rect.Top, SRCCOPY);

     bmp.Save(filename, System.Drawing.Imaging.ImageFormat.Bmp);

     // Cleanup

     bmp.Dispose();

     gxComp.Dispose();

}

 

// P/Invoke declaration

[DllImport("coredll.dll")]

public static extern int BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);

 

const int SRCCOPY = 0x00CC0020;

All you need to use it is just place the Snapshot method on your form and call it like that:

//Create a snapshot of a current view of the TextBox

Snapshot(@"\Temp\save.bmp", this.CreateGraphics(), textBox1.Bounds);

 

1/3/2006 1:44:08 PM (GMT Standard Time, UTC+00:00)  #     | 
 Friday, December 09, 2005

I've got the following info from Microsoft guys that could be helpful for anybody:

The following downloads for SQL Server 2005 Mobile Edition (SQL Server Mobile) available at http://download.microsoft.com is described below. For more information please refer to the SQL Server Mobile website at http://www.microsoft.com/sql/editions/sqlmobile.

a.    Microsoft SQL Server 2005 Mobile Edition Server Tools - The package installs the SQL Server Mobile Replications Components on the machine running the IIS server. The components are required for connecting the SQL Server Mobile database on a mobile device to SQL Server 2000 SP3a database and later versions. Link to download: http://www.microsoft.com/downloads/details.aspx?familyid=6ed0fb7e-7c05-4f59-879a-8fb619e36612&displaylang=en

b.    Microsoft SQL Server 2000 Service Pack 4 Replication Components – The package installs the SQL Server 2000 SP4 replication components on the machine running the IIS server. The components are required for connecting the SQL Mobile database on a mobile device to a SQL Server 2000 SP4 database. Link to download: http://www.microsoft.com/downloads/details.aspx?familyid=a0241f9c-79b1-4011-81e3-2601aa701e02&displaylang=en

c.     Microsoft SQL Server 2000 Service Pack 3a Replication Components – The package installs the SQL Server 2000 SP3a SQL Server 2000 SP3a replication components on the machine running the IIS server. The components are required for connecting the SQL Mobile database on a mobile device to a SQL Server 2000 SP3a database.  Link to download: http://www.microsoft.com/downloads/details.aspx?familyid=a9e0cea0-6f8d-4625-af71-fc8d671f64b6&displaylang=en

d.     Microsoft SQL Server 2005 Mobile Edition Device SDK – The SDK contains the product installers files for installing SQL Server Mobile on the mobile devices, and it also contains the DLL’s and Header files required for developing SQL Mobile applications on the Desktop. Link to download: http://www.microsoft.com/downloads/details.aspx?FamilyId=5BD8ABAA-5813-4DB3-B23A-24551DE2ECC1&displaylang=en

e.     IBuySpy Delivery 2005 Developer Toolkit Sample Application– The Toolkit is a sample e-commerce storefront to demonstrate how simple it is to create scalable applications for the Microsoft® .NET® platform by using SQL Server Mobile . Link to download: http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=7A5F64B0-4E64-4A72-A2C9-994A0C55CECD .

Apart from these the following are also available – 

EULA for SQL Server Mobile Device SDK: http://download.microsoft.com/download/2/4/8/2482E95D-00FD-4374-BD15-57A8C4C34444/EULA.rtf

ReadMe file for SQL Server Mobile Device SDK: http://download.microsoft.com/download/2/4/8/2482E95D-00FD-4374-BD15-57A8C4C34444/SSMREADME.htm

Help File for the SQL Server Mobile: http://download.microsoft.com/download/2/4/8/2482E95D-00FD-4374-BD15-57A8C4C34444/ssm3chm.chm

 

12/9/2005 2:38:47 PM (GMT Standard Time, UTC+00:00)  #     | 
 Wednesday, September 21, 2005
9/21/2005 9:50:02 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, September 08, 2005

How many times you wished to be able to use data binding with the ListView control. Well.. there are a few implementations of the bindable ListView available on the web, including the one on the MSDN. But in my opinion the best implementation was done by Ian Griffins a few years ago. In fact I've re-used this code for the ListBoxEx that's a part of SDF. So, here's the ported to .NetCF version of the BindableListView control. It doesn't have the desing-time version, but you should be able to use it in the following way:

private BindableListView bindList;

 

public Form1()

{

      //

      // Required for Windows Form Designer support

      //

      InitializeComponent();

 

      bindList = new BindableListView();

      bindList.View = View.Details;

      bindList.Size = new Size(240, 100);

      bindList.Location = new Point(0, 0);

      this.Controls.Add(bindList);

 

      bindList.DataSource = CreateData();

}

 

private ArrayList CreateData()

{

      ArrayList list = new ArrayList();

 

      Person person = new Person();

      person.Id = 4123;

      person.Name = "Alex Yakhnin";

      list.Add(person);

 

      person = new Person();

      person.Id = 5327;

      person.Name = "Chris Tacke";

      list.Add(person);

 

      //…

      return list;

}

Where the Person class is:

public class Person

{

      private int id;

      private string name;

 

      public Person()

      {

 

      }

 

      public int Id

      {

            get

            {

                  return id; 

            }

            set

            {

                  id = value;

            }

      }

 

      public string Name

      {

            get

            {

                  return name;     

            }

            set

            {

                  name = value;

            }

      }

 

}

Download the BindableListView and a test client here:

BindableListViewClient.zip (11.62 KB)
9/8/2005 3:58:26 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Monday, August 29, 2005

I've made a few updates to the code I'd posted last time mainly by moving the text wrapping functionality inside of the ToolTip control, therefore making it more self contained and useful for other scenarious. I optimized the MeasureStringExtend function to use StringBuilder and improving its speed by 50-70%. Here's the new version:

private SizeF MeasureStringExtend(Graphics g, string text, Font font, int desWidth)

{

      int sp_pos = 0;

      int line = 0;

      int nWidth = 0;

      StringBuilder sb = new StringBuilder();

 

      //get original size

      SizeF size = g.MeasureString(text, font);

 

      if (size.Width > desWidth)

      {

            string[] words = text.Split(' ');

            for (int i = 0; i < words.Length; i++)

            {

                  sb.Append(words[i] + " ");

                  nWidth = (int)g.MeasureString(sb.ToString(), font).Width;

 

                  if (nWidth > desWidth)

                  {

                        //try to find a space

                        sp_pos = sb.ToString().LastIndexOf(" ");

                        if (sp_pos > 0)

                        {

                              //cut out the wrap line we've found

                              sb.Remove(0, sb.Length);

                              sb.Append(words[i] + " ");

                              line++;

                        }

                        else //no space

                        {

                              sb.Remove(0, sb.Length);

                              line++;

                        }

                  }

            }

            // Check for the last line

            if (sb.Length > 0)

            {

                  line++;

            }

      }

      else

      {

            line++;

      }

 

      return new SizeF(desWidth, (line * size.Height));

}

 

I've also created a VS 2005 and CF v2 version of the ListViewExtender and ListViewLabel classes. Instead of ApplicationEx and IMessageFilter I utilized my NativeWindow implementation. Here's the ListViewSubclass class, the sole purpose of which to catch the WM_LBUTTONDOWN message and raise the MouseDown event:

public class ListViewSubclass : NativeWindow

{

      public event MouseEventHandler MouseDown;

 

      private const int WM_LBUTTONDOWN = 0x0201;

      private ListView listView;

 

      public ListViewSubclass(ListView listView)

      {

            this.listView = listView;

            this.AssignHandle(listView.Handle);

      }

 

      protected override void WndProc(ref Message m)

      {

            if (m.Msg == WM_LBUTTONDOWN)

            {

                  int y = HiWord((int)m.LParam);

                  int x = LoWord((int)m.LParam);

                  MouseEventArgs args = new MouseEventArgs(0, 1, x, y, 0);

                  if (MouseDown != null)

                  {

                        MouseDown(listView, args);

                  }

            }

            base.WndProc(ref m);

      }

 

      private static int HiWord(int number)

      {

            if ((number & 0x80000000) == 0x80000000)

                  return (number >> 16);

            else

                  return (number >> 16) & 0xffff;

      }

 

      private static int LoWord(int number)

      {

            return number & 0xffff;

      }

}

Download the updated VS 2003 version from here.

ListViewToolTip2003.zip (10.08 KB)

And here's the VS 2005 version for your pleasure:

ListViewToolTip2005.zip (14.25 KB)

 

 

8/29/2005 4:40:50 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Friday, August 26, 2005

The ListView control in .NetCF has been one of the most useful for me and I think for many other developers. But it lacks a few important functions like the ability to show a long text in the wrapped mode or to edit items in the ListView. So I set myself on the road of trying to accomplish this and believe me, it was not a walk in the park, rather a rock climbing.
My first thought was that the easiest way to achieve the required functionality would be just to place either a Label or a TextBox on a top of the ListView. OK, so I need the location and the size of an item in the ListView. The Windows CE SDK docs state that it is possible to send the LVM_GETITEMRECT or LVM_GETSUBITEMRECT message to the ListView control and it will return the RECT structure with what we’re looking for. But these messages expect the indexes of item and subitem to be passed. Alright, then there is the LVM_SUBITEMHITTEST message that should provide us with the LVHITTESTINFO structure. This structure includes the item’s indexes as well as the POINT structure with the expected client coordinates. Now, it all comes down to getting the coordinates of the place where the ListView is being tapped. Unfortunatelly, ListView in .NetCF doesn’t implement mouse events, so we must resort to the extreme measures like subclassing the ListView control and catching mouse down message.  As you probably know, SDF provides the ApplicationEx and IMessageFilter classes and I had done my implementation of the pseudo window procedure as the WinProcFilter class long time ago. With all pieces of the puzzle in place I was ready finish the task.
First, I have created the ListViewExtender class that implements GetItemAt and GetItemRectangle methods:

public HitItem GetItemAt(int x, int y)

{

      HitItem hitItem = new HitItem();

 

      // Init LVHITTESTINFO structure

      LVHITTESTINFO hitTextInfo = new LVHITTESTINFO();

      hitTextInfo.x = x;

      hitTextInfo.y = y;

      hitTextInfo.flags = LVHT_ONITEM;

                 

      // Allocate native memory

      IntPtr pointer = AllocHGlobal(Marshal.SizeOf(hitTextInfo));

      // Copy LVHITTESTINFO structure to the memory pointer

      Marshal.StructureToPtr(hitTextInfo, pointer, false);

                 

      // Retreive item/subitem indexes

      SendMessage(handle, LVM_SUBITEMHITTEST, 0, pointer);

 

      hitTextInfo = (LVHITTESTINFO)Marshal.PtrToStructure(pointer, typeof(LVHITTESTINFO));

      FreeHGlobal(pointer);

 

      hitItem.ItemIndex = hitTextInfo.iItem;

      hitItem.SubItemIndex = hitTextInfo.iSubItem;

                 

      return hitItem;

 

}

 

public Rectangle GetItemRectangle(int itemIndex, int subItemIndex)

{

      RECT rect = new RECT();

      rect.left = 2;

      rect.top = subItemIndex;

      // Retreive item's rectangle

      SendMessage(handle, LVM_GETSUBITEMRECT, itemIndex, ref rect);

      // Convert to the screen coordinates

      Rectangle rc = listView.RectangleToScreen(new Rectangle(rect.left, rect.top - 26, rect.right - rect.left, rect.bottom - rect.top));

      return rc;

}

Next, I wanted to have a label control that could draw borders, so whipped up a custom control ToolTip that inherits from the Control and overrides its OnPaint event:

protected override void OnPaint(PaintEventArgs e)

{

      base.OnPaint (e);

      e.Graphics.Clear(this.BackColor);

      Rectangle textRect = this.ClientRectangle;

 

      textRect.X = 3;

      //textRect.Y = 2;

      textRect.Width += 2;

      textRect.Height += 2;

 

      e.Graphics.DrawString(this.Text, this.Font, textBrush, textRect);

 

      this.DrawBorder(e.Graphics);

}

After that, I’ve create the ListViewLabel class that is derived from ListViewExtender:

public class ListViewLabel : ListViewExtender, IDisposable

{

private ToolTip toolTip;

      private WinProcFilter winProcFilter;

      private const int WM_LBUTTONDOWN = 0x0201;

      private HitItem hitItem;

      private TextBox editText;

      private bool editable;

 

      public ListViewLabel(ListView listView, bool editable) : base(listView)

      {

            this.editable = editable;

            if (!editable)

            {

                  // Create instance of the Tooltip control

                  toolTip = new ToolTip();

                  toolTip.Visible = false;

                  // Add to the Form

                  listView.Parent.Controls.Add(toolTip);

            }

            else

            {

                  editText = new TextBox();

                  editText.Visible = false;

                  editText.LostFocus+=new EventHandler(editText_LostFocus);

                  // Add to the Form

                  listView.Parent.Controls.Add(editText);

            }

 

            // Init WinProcFiter

            winProcFilter = new WinProcFilter(this.handle);

            winProcFilter.WndProc+=new WinProc(winProcFilter_WndProc);

                 

      }

}

The constructor’s parameters are a ListView control and a Boolean value that will tell the control whether you want to edit the ListView item or just show the long content. We also create instance of the WinProcFilter class and hook up into its WndProc event:

private void winProcFilter_WndProc(ref Microsoft.WindowsCE.Forms.Message m)

{    

      if (m.Msg == WM_LBUTTONDOWN)

      {

            if (toolTip != null)

            {

                  toolTip.Hide();

            }

 

            if (editText != null)

            {

                  if (editText.Visible) // Update the item's text back

                  {

                        listView.Items[hitItem.ItemIndex].SubItems[hitItem.SubItemIndex].Text = editText.Text;

                        editText.Visible = false;

                  }

            }

 

            byte[] pos = BitConverter.GetBytes(m.LParam.ToInt32());

            int x = BitConverter.ToInt16(pos, 0); // LOWORD

            int y = BitConverter.ToInt16(pos, 2); // HIWORD

 

            Point clientPoint = listView.PointToClient(new Point(x, y));

 

            hitItem = this.GetItemAt(x, y);

            if (hitItem.ItemIndex > -1)

            {

                  // Get item's rectangle

                  Rectangle rc = GetItemRectangle(hitItem.ItemIndex, hitItem.SubItemIndex);

                       

                  // Get item's text, location and width

                  string itemText = listView.Items[hitItem.ItemIndex].SubItems[hitItem.SubItemIndex].Text;

 

                  SizeF size;

                 

                  if (!editable) // display label

                  {

                        toolTip.Text = itemText;

                        toolTip.Location = new Point(rc.Left, rc.Top);

                        toolTip.Width = rc.Width;

                        // Get the height of the wrapped text

                        using(Graphics gx = listView.Parent.CreateGraphics())

                        {

                              size = this.MeasureStringExtend(gx, toolTip.Text, toolTip.Font, toolTip.Width);

                        }

 

                        if (size.Height > rc.Height) // Display a Tooltip only on multilined items

                        {

                              toolTip.Height = (int)size.Height;

                              toolTip.BringToFront();

                              toolTip.Show();

                        }

                  }

                  else // display TextBox

                  {

                        editText.Text = itemText;

                        editText.Width = rc.Width;

                        editText.Location = new Point(rc.Left, rc.Top);

                        editText.BringToFront();

                        editText.Visible = true;

                  }

            }

      }

}

In the code above we check if we received the WM_LBUTTONDOWN message. If we did, we retrieve mouse coordinates from the LParam and get the HitItem by calling created earlier GetItemAt method. After that we can fetch the item’s rectangle which is used to place the ToolTip or TextBox controls on the top of the ListView. Here are the screenshots of the test client that’s using the ListViewLabel class:

As you can see, it was not an easy road indeed.

You can download the all of the code from here:

 

ListViewToolTip.zip (10.06 KB)
8/26/2005 6:55:51 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, August 18, 2005

The .NetCF doesn't provide implementation for the DropDownWidth property for the ComboBox control. Even though it is just a simple call to send either CB_SETDROPPEDWIDTH or CB_GETDROPPEDWIDTH messages. So, following the extender pattern that I aleady used to create the ListBoxExtender I've created the ComboBoxExtender class that implements the DropDownWidth property:

public class ComboBoxExtender

{

      private ComboBox comboBox;

      private IntPtr handle;

 

      public ComboBoxExtender(ComboBox comboBox)

      {

            this.comboBox = comboBox;

            // Get native handle

            this.comboBox.Capture = true;

            this.handle = GetCapture();

            this.comboBox.Capture = false;

      }

 

      public int DropDownWidth

      {

            get

            {

                  return GetDropDownWidth();

            }

            set

            {

                  SetDropDownWidth(value);

            }

      }

 

      private void SetDropDownWidth(int width)

      {

            SendMessage(handle, CB_SETDROPPEDWIDTH, width, 0);

      }

 

      private int GetDropDownWidth()

      {

            return SendMessage(handle, CB_GETDROPPEDWIDTH, 0, 0);

      }

 

      #region P/Invokes

 

      const int CB_GETDROPPEDWIDTH    =      0x015f;

      const int CB_SETDROPPEDWIDTH    =      0x0160;

      [DllImport("coredll.dll")]

      static extern IntPtr GetCapture();

  

      [DllImport("coredll.dll")]

      static extern int SendMessage(IntPtr hwnd, int msg, int wParam, int lParam);

 

      #endregion

}

You would use this class:

private ComboBoxExtender comboExt;

 

comboExt = new ComboBoxExtender(comboBox1);

 

comboExt.DropDownWidth = 250;

 

8/18/2005 6:58:40 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Wednesday, August 17, 2005

The best way to test functionality and usability of a library is to use it for development for some real project. So I took the OpenNETCF.Rss library for a spin and created a RSS reader for Pocket PC. I gave it a name “OpenRSS”. During its development I discovered a few bugs and missing functionality in the OpenNETCF.Rss, so the time spend was worth it. Here are a few screenshots:

The OpenRSS is a work in progress. It doesn’t implement all functionality that would be required for a production ready RSS reader, so consider it as a sample or a starting point.

You can download the OpenRSS’s project with updated OpenNETCF.Rss bits here:

OpenRSS.zip (180.63 KB)

I’ve also uploaded the OpenNETCF.Rss into the Vault, which means it should become a part of SDF in the future release.

8/17/2005 5:06:07 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, August 04, 2005

The “RSS”, “blogging”, “PODcasting”, “OPML” – these acronyms are becoming increasingly big in the technology world. Especially after Microsoft had announced about RSS support in the Longhorn err… Vista and IE7. The MSDN article mentions photo blogs, moblogs, and video blogs as well as calendar feeds. But wait a second. Why does it have to run on a desktop? I am pretty sure that all these usage scenarios are perfect fit Mobile devices as well. So here’s a hint for Mobile devices / developers teams at Microsoft – let’s extend the “RSS everywhere” paradigm to the Windows CE platform as well. Call me crazy, but I also think RSS feeds could become more then an XML over HTTP. RSS feeds and lists don’t have to be blogs. They could as well include any type of business information that needs to be updated on a regular basis. What prevents us to use RSS over different communication channels like UDP or TCP?

 

So having all these ideas in mind I have started on creation of Y.A.R.F (Yet Another RSS Framework). I know there’re a few .NET implementations available on the internet, but I didn’t like either of them – they’re not flexible or extensible enough. I wanted a framework that could be used on both .NetCF and full .NET, allowed usage of the configuration files and was able to process Atom feeds as well. So here it is – a technology preview of the OpenNETCF.Rss library. When designing the architecture for the framework I’ve tried to model it after a WSE which provides paradigms of “channels” or connections and “transports“ or protocols that’re used to transport the data. Here’s the class diagram of the OpenNETCF.Rss library:

When developing the OpenNETCF.Rss I’ve used some RSS and Atom parsing code from the most excellent Dare’s RSSBandit.

 

There're a few ways you can use this library to retreive a RSS feed.

 

1. Syncronously:

 

Feed feed = FeedEngine.Receive(new Uri(“http://myfeed/rss.aspx”));

 

2. Asyncronously through event handler:

 

FeedEngine.FeedReceived+=new FeedReceivedHandler(FeedEngine_FeedReceived);

FeedEngine.Subscribe(new Uri((“http://myfeed/rss.aspx”));

 

2. Asyncronously by providing an instance of your FeedReceiver:

 

public class MyFeedReceiver : FeedReceiver

{

      Form1 form;

 

      public AlexFeedReceiver(Form1 form)

      {

            this.form = form;

      }

     

//Implement the Receive

      public override void Receive(Feed feed)

      {

            form.ShowFeed(feed);

      }

}

 

...

 

FeedEngine.Subscribe(new Uri("http://www.engadget.com/rss.xml"), new MyFeedReceiver(this));

I'll elaborate on the OpenNETCF.Rss design in a later posts.

The OpenNETCF.Rss is in no way complete. I am planning on adding more functionality like FeedStorage and communications channels. Please do not hesitate to comment on the design and implementation. The VS.NET solution includes test projects for the desktop and PPC device.

OpenNETCF.Rss.zip (406.65 KB)

Documentation.chm (100.58 KB)
8/4/2005 3:36:22 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, July 14, 2005

I've seen the request on the CF newsgroups for the control that would be able to scroll and zoom in(out) an image. Immediatedly remembered that I had this kind of control developed for my AtlasCE for Smartphone. So I pulled down the code, cleaned it up and created a sample client:

The ImageViewer control's code doesn't have a lot of comments, but I hope that the code is self explanatory enough for you to figure out how it works.

The ImageView has just a handfull of public methods properties:

You can download the control's source code and a sample from here:

ImageViewerClient.zip (76.15 KB)
7/14/2005 10:11:05 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Tuesday, July 05, 2005

This question has just come up on the .NetCF newsgroup. P/Invoke is again to the rescue:

public void SaveTextBox(TextBox textBox, string fileName)

{

      StringBuilder buffer = new StringBuilder(" ", 255);

      // Get TextBox's native handle

      textBox.Capture = true;

      IntPtr handle = GetCapture();

      textBox.Capture = false;

     

      // The EM_GETLINE requires the length of the buffer in the first byte.

      buffer[0] = (char)255;

 

      using(StreamWriter writer = new StreamWriter(fileName))

      {

            // Get line count

            int lineCount = SendMessage(handle, EM_GETLINECOUNT, 0, 0);

 

            for(int i=0;i<lineCount;i++)

            {

                  SendMessage(handle, EM_GETLINE, i, buffer);

                  writer.WriteLine(buffer.ToString());

            }

            writer.Flush();

      }

 

}

 

Required P/Invoke declarations:

const int EM_GETLINECOUNT    =  0x00BA;

const int EM_GETLINE         =  0x00C4;

 

[DllImport("coredll.dll")]

static extern IntPtr GetCapture();

 

[DllImport("coredll.dll")]

static extern int SendMessage(IntPtr hwnd, int msg, int wParam, int lParam);

 

[DllImport("coredll.dll")]

static extern int SendMessage(IntPtr hwnd, int msg, int wParam, StringBuilder lParam);

 

7/5/2005 9:57:45 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, June 23, 2005

Continuing the theme of extending the functionality of the intrinsic controls in the .NetCF, I am going to show you the way to show the horizontal scrollbar in the ListBox.

In order to achieve this we once again turn to the Windows Mobile SDK docs. The docs state that we could use the LB_SETHORIZONTALEXTENT message to set the width of the scrollbar in the ListBox. But this will work only if the ListBox is defined with the WS_HSCROLL style which we should be able to modify by employing GetWindowLong and SetWindowLong API calls. I've taken the ListBoxExtender class from the previouse post and added the following code in it:

private void ModifyStyle(uint addStyle, uint removeStyle)

{    

      // Get current window style

      uint windowStyle = GetWindowLong(handle, GWL_STYLE);

 

      if (addStyle != 0)

      {

            // Modify style

            SetWindowLong(handle, GWL_STYLE, windowStyle | addStyle);

      }

      else

      {

            // Remove style

            SetWindowLong(handle, GWL_STYLE, windowStyle & ~removeStyle);

      }

      // Let the window know of the changes

      SetWindowPos(handle, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOZORDER | SWP_NOSIZE | SWP_FRAMECHANGED);

}

 

public void SetScrollWidth(int width)

{

      // Set the scroll width

      SendMessage(handle, LB_SETHORIZONTALEXTENT, width, 0);

}

 

public bool HorizontalScrollbar

{

      get

      {

            return horizontalScrollbar;

      }

      set

      {

            if (horizontalScrollbar == value)

                  return;

            horizontalScrollbar = value;

                       

            if (horizontalScrollbar)

            {

                  ModifyStyle((uint)WS_HSCROLL, 0);

            }

            else

            {

                  ModifyStyle(0, (uint)WS_HSCROLL);

            }

      }

}

We shouldn't forget the appropriate P/Invoke declarations:

const int LB_GETHORIZONTALEXTENT  = 0x0193;

const int LB_SETHORIZONTALEXTENT  = 0x0194;

 

const long WS_HSCROLL    =      0x00100000L;

 

const int SWP_FRAMECHANGED    = 0x0020;

const int SWP_NOMOVE          =     0x0002;

const int SWP_NOSIZE          =     0x0001;

const int SWP_NOZORDER        =     0x0004;

 

const int GWL_STYLE         =  (-16);

 

 

[DllImport("coredll.dll")]

static extern int SendMessage(IntPtr hwnd, int msg, int wParam, int lParam);

[DllImport("coredll.dll")]

static extern uint GetWindowLong(IntPtr hwnd, int index);

[DllImport("coredll.dll")]

static extern void SetWindowLong(IntPtr hwnd, int index, uint value);

[DllImport("coredll.dll")]

static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,

      int Y, int cx, int cy, uint uFlags);

You can download the full version of the ListBoxExtender here:

ListBoxExtender.zip (1.35 KB)

After the code's updated, you can use the ListBoxExtender class the following way:

listBoxEx = new ListBoxExtender(listBox1);

...

listBoxEx.HorizontalScrollbar = true;

listBoxEx.SetScrollWidth(200);

Now, the most attentive should be able to notice that I am passing some hardcoded value to the SetScrollWidth method, which of course is not going to work for all possible cases. In order to have an appropriate behaviour of the scrollbar you will have to indentify the longest string item in your ListBox and pass it to the SetScrollWidth .

6/23/2005 10:10:20 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Wednesday, June 22, 2005

As you know, the intrinsic controls (ListBox, TextBox, ComboBox etc..) in the .NET Compact Framework are just a wrappers around its native counterparts. It is also known that the managed versions do not expose all available functionality. For example, it is possible to search the items in the ListBox or ComboBox just simply sending a windows messages such as LB_FINDSTRINGEXACT or LB_FINDSTRING. In order to illustrate its usage I've created the ListBoxExtender class, that will expose Search methods in the easy to use form:

/// <summary>

      /// Summary description for ListBoxExtender.

      /// </summary>

      public class ListBoxExtender

      {

            private ListBox listBox;

            private IntPtr handle;

 

            public ListBoxExtender(ListBox listBox)

            {

                  this.listBox = listBox;

                  // Get native handle

                  this.listBox.Capture = true;

                  this.handle = GetCapture();

                  this.listBox.Capture = false;

            }

 

            /// <summary>

            /// Searches an item in the ListBox by the specified item value.

            /// </summary>

            /// <param name="item">Item to search.</param>

            /// <returns>The index of the found item.</returns>

            public int Search(string item)

            {

                  return Search(item, -1, true);

            }

 

            /// <summary>

            /// Searches an item in the ListBox by the specified item value.

            /// </summary>

            /// <param name="item">Item to search.</param>

            /// <param name="startIndex">The zero-based index of the item before the first item to be searched.</param>

            /// <returns>The index of the found item.</returns>

            public int Search(string item, int startIndex)

            {

                  return Search(item, startIndex, true);

            }

 

            /// <summary>

            /// Searches an item in the ListBox by the specified item value.

            /// </summary>

            /// <param name="item">Item to search.</param>

            /// <param name="startIndex">The zero-based index of the item before the first item to be searched.</param>

            /// <param name="exact">Specifies whether the exact or partial search is performed.</param>

            /// <returns>The index of the found item.</returns>

            public int Search(string item, int startIndex, bool exact)

            {

                  int result = -1;

 

                  if (exact)

                  {

                        result = SendMessage(handle, LB_FINDSTRINGEXACT, startIndex, item);

                  }

                  else

                  {

                        result = SendMessage(handle, LB_FINDSTRING, startIndex, item);

                  }

 

                  return result;

            }

 

            #region P/Invokes

 

            const int LB_FINDSTRING           = 0x018F;

            const int LB_FINDSTRINGEXACT      = 0x01A2;

 

            [DllImport("coredll.dll")]

            static extern IntPtr GetCapture();

            [DllImport("coredll.dll")]

            static extern int SendMessage(IntPtr hwnd, int msg, int wParam, string lParam);

 

            #endregion

      }

The usage of this class should be obviouse:

listBoxEx = new ListBoxExtender(listBox1);

...

listBox1.SelectedIndex = listBoxEx.Search("Some Item");

 

6/22/2005 5:48:35 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Wednesday, June 01, 2005

Managed controls in the Compact Framework don't expose all available functionality of the native controls they wrap. And that is understandable since the CF team wanted to make them fast and small. For example, ComboBox control doesn't implement the DropDown event that could be quite helpfull to store a previouse value because it should fire before the SelectedIndexChanged event. So I've decided to implement this event using the NativeWindow class I blogged about the other day.

The Windows CE SDK documentation states that there's a CBN_DROPDOWN notification message that is send to the parent of the ComboBox when the listbox of a combo box is about to become visible. This message is sent in the form of WM_COMMAND.

A quick trip to the winuser.h file to pull out the values for the CBN_DROPDOWN and WM_COMMAND and here's the ComboSubclass class that catches this message and raises the DropDown event:

public class ComboSubclass : NativeWindow

{

    private const int WM_COMMAND = 0x111;

    private const int CBN_DROPDOWN   =  7;

 

    public event EventHandler DropDown;

 

    private ComboBox comboBox;

 

    public ComboSubclass(ComboBox comboBox, Control parent)

    {

        this.comboBox = comboBox;

        //Subclass parent form

        this.AssignHandle(parent.Handle);

    }

 

    protected override void WndProc(ref Message m)

    {

        if (m.Msg == WM_COMMAND)

        {

            // Make sure that's its our ComboBox

            if (m.LParam == comboBox.Handle)

            {

                if (DropDown != null)

                {

                    // Raise the event

                    DropDown(comboBox, null);

                }

            }

        }

        base.WndProc(ref m);

    }

}

The usage of this class should be pretty straightforward:

   comboSubclass = new ComboSubclass(comboBox1, this);

  comboSubclass.DropDown += new EventHandler(comboSubclass_DropDown);

           

 

  void comboSubclass_DropDown(object sender, EventArgs e)

  {

      prevIndex = ((ComboBox)sender).SelectedIndex;

  }

Update: You can achieve the same goal in the CF v1 by utilizing ApplicationEx class from SDF and WinProcFilter I posted last year.

6/1/2005 2:14:42 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Monday, May 23, 2005

As you probably know, the CF team has added the capability to use managed delegates as a native callbacks in the CF v2. It means that it’s become quite possible to use API’s that require a callback function such as EnumFonts, EnumWindows etc… The same goes true for been able to use SetWindowLong to subclass window procedure. The full .NET Framework includes a very useful NativeWindow class that wraps window subclassing and creating new windows functionality in the very usable and convenient package. A few months ago, I’d created a .NET Compact Framework v2 implementation of the NativeWindow class with exactly the same functionality (AssignHandle, CreateHandle, WndProc etc...

 

So what can you do with this class? Subclass any control and catch the messages that are not exposed as events, create your own versions of the native controls (EDIT, LISTBOX, ListView etc…)

 

You should expect the NativeWindow to become a part of the Smart Device Framework for CF v2.

 

You can download the NativeWindow class and a test project from here:

NativeWindowTest.zip (62.02 KB)
5/23/2005 5:28:13 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, May 05, 2005

One of my favorite new features available in the CF v2 is the XmlSerializer. Presence of it in the CF opens up a great possibilities in building a cross device/desktop communications (read WSE and such). But when you start using it you'd notice some lag when trying to serialize/deserialize your objects. It's explained by a way the XmlSerializer is designed. When you call its constructor:

new XmlSerializer(type, defaultNamespace)

the serializer walks the whole object graph using reflection and discovers all serializable properties and fields. Reflection as you know, due to its “late bound“ nature, is not the fastest part of .NET. So the solution could be caching the XmlSerializer for the object types that already have been used and re-use it for next time you need the serializer.

Here's the code for XmlSerializerCache class that you can use to achieve that:

public class XmlSerializerCache

{

      #region fields

 

      private static Hashtable table;

 

      #endregion // fields

           

      #region constructors

 

      static XmlSerializerCache()

      {

            XmlSerializerCache.table = new Hashtable();

      }

 

      private XmlSerializerCache()

      {

      }

 

      #endregion // constructors

 

     

      public static XmlSerializer GetXmlSerializer(Type type, string defaultNamespace)

      {

            XmlSerializer serializer;

            if (type == null)

            {

                  throw new ArgumentNullException("type");

            }

            // Make it thread safe

            lock (XmlSerializerCache.table.SyncRoot)

            {

                  string typeName;

                  if ((defaultNamespace == null) || (defaultNamespace.Length == 0))

                  {

                        typeName = type.FullName + "#";

                  }

                  else

                  {

                        typeName = type.FullName + "#" + defaultNamespace;

                  }

                  // Try to get the serializer from cache

                  object obj = XmlSerializerCache.table[typeName];

     

                  if (obj == null)

                  {

                        // We don't have it, create a new instance

                        obj = new XmlSerializer(type, defaultNamespace);

                        XmlSerializerCache.table.Add(typeName, obj);

                  }

                  serializer = obj as XmlSerializer;

            }

            return serializer;

      }

}

 

The usage of the XmlSerializerCache should be pretty straightforward:

 

XmlSerializer serializer = XmlSerializerCache.GetXmlSerializer(typeof(Order), "http://tempuri.org/");

 

serializer.Serialize(stream, order);

 

BTW, you can employ this class for the desktop .NET as well too and enjoy some speed improvements.

5/5/2005 4:31:20 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Friday, February 04, 2005

In his recent post Mike Zintel divuldges some information on the past, present and future of evolution of the NetCF and asking some feedback on the priorities for v.Next. As I already pointed out in my previouse post, CF lacks essential support for developing SOA such as WS-* protocol suite or Indigo. So seeing that CF team is thinking on leveraging it is very encoraging sign. The only problem I see here is the timing. Mike mentions this in the context of V3, but we need it now. The project that I am working on is a real time trading system that utilizes desktop as well as CE devices clients. In the case of desktop the usage of WSE is no brainer and fits in the architecture that I designed pretty well. But in case of CE devices I have to go through some loops by utilizing a Web Service in the middle for outgoing messages and a custom UDP communication for incoming. Plus by the time the CF V3 will be coming out the Indigo should be available so it would make sence to implement Indigo for CF V3 as well. How about if CF team would release a WS-* for CF as and add-on not tied up to V3, but may be to some Service Pack for v2 of CF and provided a full support for Indigo in the V3. I think this should be a feasable option since MS already has the WSE in the v2.0 which is pretty stable and shouldn't pose a lot problems when porting the code to CF v2.

2/4/2005 3:32:23 PM (GMT Standard Time, UTC+00:00)  #     | 
 Wednesday, January 12, 2005

You should be aware that it's not feasible to call EnumWindows API in the CF, since the usage of the delegates as a native callbacks is not supported in the v1 (it will be supported in CF v2 though). But there's a way to emulate functionality of this API by using GetWindow and GetWindowText:

public static Window[] EnumerateTopWindows()

{

      ArrayList winList = new ArrayList();

      IntPtr hwnd = IntPtr.Zero;

      Window window = null;

      StringBuilder sb = null;

                 

      // Get the first window

      hwnd = GetActiveWindow();

      hwnd = GetWindow(hwnd, GW_HWNDFIRST);

     

      while(hwnd != IntPtr.Zero)

      {    

            IntPtr parentWin = GetParent(hwnd);

            // Make sure that the window doesn't have a parent

            if ((parentWin == IntPtr.Zero))

            {

                  int length       = GetWindowTextLength(hwnd);

                  // Check if it has caption text

                  if (length > 0)

                  {

                        window = new Window();

                        window.Handle = hwnd;

                        sb = new StringBuilder(length + 1);

                        GetWindowText(hwnd, sb, sb.Capacity);

                        window.Caption = sb.ToString();

                        winList.Add(window);

                  }

            }

                 

            hwnd = GetWindow(hwnd, GW_HWNDNEXT);

      }

      return (Window[])winList.ToArray(typeof(Window));

}

 Download the sample code from here.

EnumerateWindowsProj.zip (5.82 KB)
1/12/2005 5:03:23 PM (GMT Standard Time, UTC+00:00)  #     | 
 Wednesday, December 29, 2004

As you know CF v1 (it will be available in CF v2 though) doesn't provide implementaion of the FromHbitmap method that allows creation of the managed Bitmap object from native window handle (HBITMAP). The solution that I provide is based on the idea of creation of a managed Bitmap from an in memory array/stream of a bitmap file like for example is done in the XrossOne Mobile GDI+. We can get the information on the Bitmap using GetObject API including a pointer to the bitmap bits which we can copy to the managed array:

public static Bitmap FromHbitmap(IntPtr handle)

{

     BITMAP bm = new BITMAP();

     // Get info

     GetObject(handle, Marshal.SizeOf(bm), ref bm);

     // Create our Bitmap buffer

     BitmapBuffer bmpBuffer = new BitmapBuffer(bm.bmWidth, bm.bmHeight, (short)bm.bmBitsPixel);

     // Native pointer to bmp bits

     IntPtr pBits = new IntPtr(bm.bmBits);

     // Create temp array for bitmap bits to workaround a bug in the Marshal.Copy -

     // it never uses the startIndex

     byte[] tempBits = new byte[bm.bmWidthBytes * bm.bmHeight];

     //Get the bits to the temporary managed byte array.

     Marshal.Copy(pBits, tempBits, 0, bm.bmWidthBytes * bm.bmHeight);

     // Copy from the temporary managed byte to our buffer

     System.Buffer.BlockCopy(tempBits, 0, bmpBuffer.buffer, offset, tempBits.Length);

     // Create managed Bitmap

     return bmpBuffer.CreateBitmap();

}

You can download the whole implementation for the BitmapBuffer class and the sample here.

FromHbitmapProject.zip (6.63 KB)
12/29/2004 10:42:10 PM (GMT Standard Time, UTC+00:00)  #     | 
 Friday, December 10, 2004

 

There’s a known solution to enable HTTP 1.1 Compression for Web Services that’s been described on dotnetjunkies site. The solution that they propose is to override GetWebRequest and GetWebResponse methods in your WS generated proxy file. If you’d try to implement this solution for CF you’d get an “no suitable method found to override" exception. The problem is that Microsoft “forgot” to expose these methods as overridable in the WebClientProtocol class in the v1 of CF, but… they have silently updated the WebClientProtocol class to expose the GetWebRequest and GetWebResponse as virtual (overridable) methods in one of the Service Packs.

So, in order to be able to compile the sample from dotnetjunlies, you'd need to update the System.Web.dll in the \CompactFrameworkSDK\v1.0.5000\Windows CE directory. Here's the tricky part. The steps are:

 

1. Download the CF SP2 developer redistributable:

 

http://www.microsoft.com/downloads/details.aspx?familyid=10600643-09b3-46d8-ba28-bc494bc20d26&displaylang=en

 

2. Unpack one of the CAB's from the distribution (I've used WinRAR to unpack the netcf.core.ppc3.x86.CAB file).

 

3. Rename the SYC6B2~1.014 file to System.Web.dll

 

4. Copy it to the fore mentioned directory.

 

Here you go, now you should be able to modify the web service proxy files to use using Standard HTTP 1.1 Compression.

 

Oh… and also make sure you’re running the SP2 bits on the device as well.

 

I’ve put together a CF sample that includes SharpZipLib and the code from the article. The sample uses the StockService sample Web Service from WSE 2.0 distribution.

 

You can download the sample here.

WSCompression.zip (226.13 KB)
12/10/2004 4:11:00 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, December 07, 2004

A question has recently popped up on CF newsgroups on how to connect to a network resource programmatically. There's no managed API's exist to handle that, but as always native API's and P/Invoke comes to our rescue. On a desktop as well as in Windows CE this functionality's been handled by a network API's such as WNetAddConnection3, WNetCancelConnection2 etc... The documentation on them is pretty straightforward.

[DllImport("coredll")]

private static extern int WNetAddConnection3(

                  IntPtr hwndOwner,

                  NETRESOURCE lpNetResource,

                  string lpPassword,

                  string lpUserName,

                  int dwFlags);

 

[DllImport("coredll")]

private static extern int WNetCancelConnection2(

                  string lpName,

                  int dwFlags,

                  int fForce);

The WNetAddConnection3 accepts as a parameter the pointer to the NETRESOURCE structure so I declared it as the following:

private class NETRESOURCE

{

      public int dwScope;

      public int dwType;

      public int dwDisplayType;

      public int dwUsage;

      public IntPtr lpLocalName;

      public IntPtr lpRemoteName;

      public IntPtr lpComment;

      public IntPtr lpProvider;

}

In order to assign the values to lpLocalName and lpRemoteName members we'll use the MarshalEx.StringToHGlobalUni call from SDF. So, the call to WNetAddConnection3 looks like that:

NETRESOURCE NetRes = new NETRESOURCE();              

      NetRes.dwScope = RESOURCE_GLOBALNET | RESOURCE_REMEMBERED;

      NetRes.dwType = RESOURCETYPE_DISK;

      NetRes.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;

      NetRes.dwUsage = RESOURCEUSAGE_CONNECTABLE;

      NetRes.lpRemoteName = MarshalEx.StringToHGlobalUni(netRes);

      NetRes.lpLocalName = MarshalEx.StringToHGlobalUni(shareName);

      NetRes.lpComment = IntPtr.Zero;

      NetRes.lpProvider = IntPtr.Zero;

                       

      int ret = WNetAddConnection3(hwnd, NetRes, password, userName, 1);

 

      if (ret != 0)

      {

            throw new System.ComponentModel.Win32Exception(ret, ((NetworkErrors)ret).ToString());

      }                      

 After you've made successfull mapping you should be able to access your network resource by using System.IO's functionality like File.Copy, File.Create, File.Delete etc... Keep in mind that since Windows CE does not support drive letters, your share name will be mapped to the reserved root \Network. So if you named your share as “REMOTE“, you'll access it as “\Network\REMOTE\somefile.txt”.

You can download the source code for the Network class and a sample here.

MapDriveProject.zip (7.34 KB)
12/7/2004 3:25:23 PM (GMT Standard Time, UTC+00:00)  #     | 
 Monday, December 06, 2004

I've updated the SIPControl to draw images in place of buttons. It required just minimal changes to the code. I added Image and ImageDown properties to the KeyButton class, and added an new override to the DrawButton method to support image drawing:

private void DrawButton(string text, Graphics gx, Rectangle rc, Color foreColor, Image image)

{

      Brush foreBrush = new SolidBrush(foreColor);

     

      // draw image

      gx.DrawImage(image, rc.Left, rc.Top);

      //Center the text

      SizeF size = gx.MeasureString(text, this.Font);

           

      int X = rc.Left + (int)(rc.Width - size.Width)/2;

      int Y = rc.Top + (int)(rc.Height - size.Height)/2;

                 

      gx.DrawString(text, this.Font, foreBrush, X, Y);

                 

      foreBrush.Dispose();

}

I've also modified the OnPaint method to recognize if the KeyButton has an image assigned. So now, if you'd want to have image to draw a button, the client code would look like that:

ButtonRow row1 = new ButtonRow(new string[]{"q","w","e","r","t","y","u","i","o","p","[","]"}, 5, 20);

row1.Space = 0;

//Assign images

foreach(KeyButton button in row1.Buttons)

{

      button.Image = bitmap;

      button.ImageDown = bitmapDown;

}

And here's the screen shot of the sample:

Download the new version of the SIPControl and a sample here.

SIPTest1.zip (10.05 KB)

12/6/2004 5:51:37 PM (GMT Standard Time, UTC+00:00)  #     | 
 Friday, December 03, 2004

As you probably know that if you would want to implement a full “kiosk” mode on Pocket PC – which means that you need to hide the “Start” and SIP buttons to prevent users getting into underlying system. It’s easy to employ – just remove a MainMenu from a Form, set ControlBox to false and set the WindowState = FormWindowState.Maximized in the form’s Load event. But what if I still need clients to use SIP to enter some text? We could use the SipShowIM API, but this call will make the SIP button to appear again breaking the “kiosk” mode. So, to solve this puzzle I’ve created the SIPControl  - an owner-drawn control that allows creation of any type of SIP’s. Here’s the code snip that shows how to create a full SIP keyboard on your form:

sipFull = new SIPControl();

                 

// Location and size

sipFull.Location = new Point(1, 60);

sipFull.Size =    new Size(240, 90);

     

//Create ButtonRows

ButtonRow row1 = new ButtonRow(new string[]{"Q","W","E","R","T","Y","U","I","O","P"}, 1, 0);

row1.Space = 4;

// Add ButtonRow to the Rows collection

sipFull.Rows.Add(row1);

ButtonRow row2 = new ButtonRow( new string[]{"A","S","D","F","G","H","J","K","L"}, 10, 22);

row2.Space = 4;

sipFull.Rows.Add(row2);

ButtonRow row3 = new ButtonRow( new string[]{"Z","X","C","V","B","N","M"}, 30, 44);

row3.Space = 4;

sipFull.Rows.Add(row3);

ButtonRow row4 = new ButtonRow(  new string[]{"1","2","3","4","5","6","7","8","9","0"}, 1, 66);

row4.Space = 4;

sipFull.Rows.Add(row4);

                 

// Hook up the Click event

sipFull.ButtonClick+=new ButtonClickEventHandler(sipFull_ButtonClick);

this.Controls.Add(sipFull);        

 And here’s how the sample looks:

 

You can download the SIPControl and a sample from here.

SIPTest.zip (7.8 KB)
12/3/2004 4:44:53 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, November 23, 2004

In his recent post Alex Feinman shows how to create XmlDataSource. This is a really great and usefull piece of code that shows how to implement databinding by the means of xml file.

If your your XML data is not that complex and close to the DataSet's xml serialized schema you can use the Paul Wilson's Open-Source ADO.NET XML Provider, that implements appropriate .NET Framework Data Provider interfaces and allows accessing and filtering the XML data by way of sql statements. I've ported the code for Compact Framework. Here's the screen shot of a sample client:

You can download the source code from here.

XmlDbProvider.zip (147.42 KB)

 

11/23/2004 7:48:30 PM (GMT Standard Time, UTC+00:00)  #     | 
 Wednesday, November 17, 2004

Chris Forsberg of pocketpcdn reminds us about this really cool resource created by Microsoft: We Rock 247. The Smart Client Sample download includes all required SDK's, tools and a source code for web client, winform client, web services and mobile client. The mobile part includes a few applications for Pocket PC and Smartphone (Packman and RoadRunner) and shows how to implement MVC pattern, MD5 password hashing, exception tracing, database wrapper (a la stored procedures), forms stack and many more really useful stuff.

11/17/2004 7:25:36 PM (GMT Standard Time, UTC+00:00)  #     | 
 Monday, October 18, 2004

Everybody has been talking about Service-Oriented Architecture (SOA) lately. And it’s not about Web Services, but essentially about service been abstracted from the implementation, published specs of its interface and a formal contract between service provider and its customer.  So, mostly it is about standards in the communication between different platforms and systems – this is where SOAP, WSE (Indigo in the future) come to play. It’s all nice and dandy when you develop SOA systems for desktops/servers, but we’d immediately step into uncharted territory if you try to apply SOA principals when developing for mobile devices. Yes, we can call to Web Services from .NET CF client, we can even communicate with servers which implement WSE (big thanks to Casey for his wonderful work here), but you’d fall short if your project should require a 2 way real-time communication with a desktops or other devices in the almost always connected (WIFI) environment. So this rant is to bring attention of Microsoft folks on this very important subject. Mobile devices are not only used in the end-user retail environment, but also in the enterprise. We need WSE or Indigo for mobile devices.

10/18/2004 3:03:05 AM (GMT Daylight Time, UTC+01:00)  #     | 
 Tuesday, September 14, 2004

In this old post of mine I was asking for more access to the native windows and graphics handles in CF. Things are really getting better in the Compact Framework V2. We get what we were asking for.  The Graphics.FromHdc is there, as well as Bitmap.LockBits (UnlockBits) and Image.Save. Although the Bitmap.GetHbitmap and Image.FormHandle are still seems to be missing.

We still don't have the ability to access WinProc of the managed control or to host a native control in the managed environment, but hopefully this will be changed when (if) CF V2 will support native callbacks.

9/14/2004 2:34:51 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Wednesday, September 08, 2004

A lot of questions on inter process communication (IPC) in Compact Framework have recently popped up on the CF NG. And one of the options for IPC is to use Memory Mapped Files (MMF). So what are those? Here's the quote from MSDN documentation:

“A memory-mapped file, or file mapping, is the result of associating a file's contents with a portion of the virtual address space of a process. It can be used to share a file or memory between two or more processes...”

Windows CE supports both named and unnamed file-mapping objects. I'd say that named object to be more useful.  If the created object has a name you can easily pass that name to other processes so these processes can access the same object.

While doing investigation on my current project possibility of IPC, I've made some research on available C# code that wraps MMF. I've found quite a few implementations available on various web sites (CodeProject.com, Gotdotnet.com, etc...), but I've decided to port the implementation of MemoryMappedFileStream that comes with the Smart Client Offline Application Block. The conversion to CF was easy - changed the import dll to coredll.dll and added some functionality to handle named MMF's.

You can download the project from here.

9/8/2004 6:52:00 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, August 26, 2004

While doing modeling for the new project that requires a HTTPS communication by posting some xml messages, I've encountered a few intricacies in the Compact Framework. First problem I’ve hit when trying to use the code that’s been working fine for me in the previous project to send a POST request over SSL is the known bug that throws a Socket exception. To workaround it I had to stop using ContentLength of the request class and set the AlllowWriteStreamBuffering to true. Another problem I ran into is that I was getting a TrustFailure exceptions due to a security certificate on the web server I was accessing is not fully trusted. A quick search on the Google brought up a great post by Jan Tielens that suggested implementing the ICertificatePolicy interface. It worked like a charm. The last problem came into play when I discovered that the web site requires passing a session cookies after logging in. That was easily resolved by getting the cookies from the WebResponse.Headers collection and setting it back in the request object. Here’s the snippet of the final working code:

request = (HttpWebRequest)WebRequest.Create(url);

request.Method = "POST";

request.ContentType="application/x-www-form-urlencoded";

//request.ContentLength = parameters.Length;

request.AllowWriteStreamBuffering  = true; //required for bug workaround

request.Timeout = 60000;

     

if (sessionCookies!="")

{    

      string[] cookies = sessionCookies.Split(';');

      // We've got some cookies

      if (cookies.Length > 0)

      {

            request.Headers.Add("Cookie", cookies[0]);

      }

      else

      {

            request.Headers.Add("Cookie", sessionCookies);

      }

}

//Get request stream

Stream requestStream = request.GetRequestStream();

//Write a post values

requestStream.Write(Encoding.ASCII.GetBytes(parameters), 0, parameters.Length);

requestStream.Close();

// Get response

response = (HttpWebResponse)request.GetResponse();

if (sessionCookies == "")

{

      //Get the cookies

      sessionCookies = response.Headers["Set-Cookie"];

}

 

reader = new StreamReader(response.GetResponseStream());

result = reader.ReadToEnd();

response.Close();

 

8/26/2004 2:25:12 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, July 15, 2004

I've been busy these days with a new major CF project at some famous company, but I don't want you to miss a few important and interesting things happening on the CF horizon:

1. I am sure you're seen the announcement about OpenNETCF Coding Competition.

2. Mark Clifton of the MyXaml fame has created a forum for using MyXaml with the Compact Framework. You can already download the bits for CF. Very interesing stuff!

3. How about a Class Browser running on the device?

7/15/2004 4:02:28 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Monday, June 28, 2004

Seth's posted an invaluable info on Managed App Startup and Retargeting. I wish we had more of these “brain dumps“.

 

6/28/2004 4:30:39 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Thursday, June 24, 2004

I've just posted a new article on OpenNETCF.org: Creating a Custom Marshaler for the .NET Compact Framework

Enjoy...

 

6/24/2004 8:40:15 PM (GMT Daylight Time, UTC+01:00)  #     | 

David Kline is blogging about possibility to use cordbg to debug .NetCF. Really great stuff! He's just posted the part II with explanation of modes and commands when debugging. And more info will be coming in! My only gripe is why this information could not be available earlier.

6/24/2004 1:26:53 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Wednesday, June 02, 2004
The recent competition at Codeproject.com produced a slew of motivating applications for Compact Framework. Some of them show quite interesting solutions (many of them by the way directly reference or reuse the code from SDF) in resolving the challenges of the device development.  My favorites are:

Apply Animated Effects to .NET Controls

Thin Client for Microsoft Visual SourceSafe on Pocket PC / SmartPhones

Pocket PC based Animation Editor

Wireless Pocket PC media Remote for PC

ForestPad - a method for storing and retrieving textual information

6/2/2004 1:51:31 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Wednesday, May 12, 2004

I've been following a heated discussions on XML parsing methods that exist in .NET right now: DOM and XmlReader, whereas the first one is the slowest and the second is fast but a more complicated to code against. A must read on this topic is the DonXML's post Waking Up From a DOM Induced Coma who underlines the existing problems with XML parsing and mentions a few other alternatives. All of the problems about XML parsing are relevant to .NetCF as well, therefore they are multiplied due to processing power and memory constraints of the WinCE devices. Don't forget a complete lack of XPath implementation in the current version of CF.

As lucky as we are, Dare Obasanjo and Co., a PM from Microsoft, have just posted another library: XPathReader. And as you can guess, I've immediately pulled the code from the article and tried to compile it against .NetCF... The result of the test was that it runs with a few minor changes to the original code!!! And it is more exiting since we get a some XPath functionlity as well. Here you can see a snapshot of the test program running in the PPC emulator:

Don't you love when something useful comes with the source?

You can download the test and a XPathReaderCF projects from here.

The next step will be to test a comparison performance of this library vs XmlDocument in CF.

5/12/2004 5:30:34 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Tuesday, May 11, 2004

One of the FAQ on the CF newsgroup has been a question on how to send email from WinCE device with attachments or without it. We know that on Pocket PC's emailing functionality is a part of POOM API's as well as exposed in some form of MAPI implementation. All of these options are not easy to implement from .NetCF.  Plus POOM is not available on non PPC devices... But how about just using a SMTP protocol over TCP/IP connection? A quick search on the internet returned quite a few .NET implementations. Due to its simplicity I decided to go with Peter Bromberg's excellent article as a starting point. The code in the article references missing from .NetCF MailMessage and MailAttachment classes which shouldn't be a big problem to implement... Here is the screenshot of the testing program:

A quick test showed that I can successfully send attachments as well.

Download the code here.

5/11/2004 3:22:31 PM (GMT Daylight Time, UTC+01:00)  #     | 
 Tuesday, March 23, 2004

Just to prove in the hundred's time that there's nothing impossible exist for him, Alex Feinman has posted this article on OpenNETCF.org. It shows how to create a managed Icon object from native icon handle. Brilliant job (as always) Alex! 

3/23/2004 8:14:19 PM (GMT Standard Time, UTC+00:00)  #     | 

Brad is describing some new stuff coming with Whidbey for GC. Considering a much more memory constrained environment of CE devices, GC.AddMemoryPressure/GC.RemoveMemoryPressure and HandleCollector would be really nice to have for Compact Framework too!

3/23/2004 3:55:36 PM (GMT Standard Time, UTC+00:00)  #     | 
 Friday, March 19, 2004

I've finally released AtlasCE for SmartPhone 2003 which I think is one of the first commercial .NET CF programs for Smartphones. Aside from learning the intricacies of the smartphone UI, I had to create a key-press searching/filtering mechanism similar to the built-in Contacts application, a scrolling image viewer control with zooming functionality and other more interesting stuff. It was always like that for me - creating some program would lead to creation of a few “by-products” that could be utilized later on in some other applications. I'll try to show you how it's all been done in a later blog entries. Meanwhile I can tell you that I've used a lot of code from SDF: ListBoxEx, Configuraton and HTMLViewer.  

3/19/2004 8:28:03 PM (GMT Standard Time, UTC+00:00)  #     | 
 Thursday, March 18, 2004

Alright, so I've got this new and shiny blog now! Big thanks to Neil for spending his valuable time converting me to the dasBlogs. It has so much stuff that I will have to spend some time looking around.

Meanwhile, back to the subject.

In Windows programming there is a notion of Virtual ListBoxes or ListViews. It means that a list box would allow you to have unlimited amount of the displayed items. It is usually done by telling it how many items there should be and it simply delegates all drawing of items to the owner of the list box. Under Windows CE the list box can’t be made virtual, only a ListView. But it should be really easy to implement by utilizing OwnerDrawnList base control, which I described here and which has become a part of SDF.

So, here are the steps to implement the VirtualListBox:

 

  1. Derive from OwnerDrawnList to create a new class:

public class VirtualListBox : OwnerDrawnList

{

// Private members

      int count;

      private ArrayList itemList;

      // Constructor

      public VirtualListBox()

      {

            itemList = new ArrayList();

            this.ShowScrollbar= true;

      }

 

      // Property to the number of items

      public int Count

      {

            get

            {

                  return count;

            }

            set

            {

                  if (count!=value)

                  {

                        count = value;

                        itemList.Clear();

                        int[] items = new int[count];

                        itemList.AddRange(items);

                  }

            }

      }

 

      // if you don’t want to draw the item yourself just use this method

      public void DefaultDrawItem(DrawItemEventArgs e, String text)

      {

            Brush textBrush;

 

            Rectangle rcText = e.Bounds;

 

            //Add 3 pixels

            rcText.X += 3;

 

            if (e.State == DrawItemState.Selected) // selected                      {

                  e.DrawBackground();

                  textBrush = new SolidBrush(SystemColors.HighlightText);

            }

            else

            {

                  e.DrawBackground(this.BackColor);

                  textBrush = new SolidBrush(SystemColors.WindowText);

            }

 

// Draw the string

            e.Graphics.DrawString(text, this.Font, textBrush, rcText);

                 

            // Dispose the brush

            textBrush.Dispose();

 

      }

 

      // This is the required by base class override

      protected override System.Collections.IList BaseItems

      {

          get

          {

             return itemList;

          }

      }

           

       // This one required by base class too, but it will do nothing for us.