Friday, January 30, 2004

One of my wild google searches today have returned this jem: Compact Framework Architecture. This is a presentation by Frank Perchel-Galee which reviews a CF architecture. The most interesting slides for me are the ones that show the memory pools in the CLR. There 3 memory pools allocated: Metadata Cache, JIT Cashe and GC Pools. Memory reclaiming by GC is happenning in 3 phases:
Phase1: Mark and sweep
Phase2: Compact whatever is left
Phase3: Flush JIT Cache

That could mean that if your program requires quite a lot of memory to run, the once JIT'ed code could be flushed and would force JITer to kick in again for the code that's already been JIT'ed. One more interesting bit from one of the slides says that the compile time of JIT compiler is LINEAR to length of the method. This leads to immediate conclusions that if you want to get a good performance from your CF app, try to brake your long winded methods to a few shorter ones. It could save you some time during execution...

 

Oh and don' forget this MSDN article Writing High Performance Managed Applications. Most of the stuff mentioned there is applicable to CF too.

1/30/2004 2:02:55 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, January 27, 2004

As you know, the program memory on Pocket PC and Smartphone devices supposed to be handled by the OS itself. Meaning that shell would start sending WM_CLOSE messages if there is less than 128 KB of free memory. In some cases when running you application before allocation of some big chunk of memory you'd need to make sure that there's enough memory available for that operation. There is the SHCloseApps API function in the aygshell library that will do just what you need. We could also utilize the GlobalMemoryStatus to verify if such a drastic measures are required:

public bool FreeProgramMemory(int memSought)

{                

      bool result = true;

      MEMORYSTATUS memStatus = new MEMORYSTATUS();

      GlobalMemoryStatus(ref memStatus);

      //check if we're out of memory first

      if( memStatus.dwAvailPhys < memSought)

      {

            if (SHCloseApps(memSought)!= 0)

                  result = false;

      }

      return result;

}

//Required P/Invoke declarations

[DllImport("aygshell.dll")]

public static extern int SHCloseApps(int  dwMemSought);

 

[DllImport("coredll.dll")]

public static extern  void GlobalMemoryStatus(

            ref MEMORYSTATUS lpBuffer );

 

public struct MEMORYSTATUS

{

      public int dwLength;

      public int dwMemoryLoad;

      public int dwTotalPhys;

      public int dwAvailPhys;

      public int dwTotalPageFile;

      public int dwAvailPageFile;

      public int dwTotalVirtual;

      public int dwAvailVirtual;

}

 

1/27/2004 2:20:36 PM (GMT Standard Time, UTC+00:00)  #     | 
 Thursday, January 22, 2004

I don't know how about you, but for me visiting The Code Projects at least once a day have become a pleasant and necessary habit. It's really a valuable web site with a wealth of useful samples and projects for .NET. So, the other day I've stumbled upon this article on the web site. It explains how to create thumbnail images from a directory of Adobe Acrobat PDF documents.  The author gives details on a technique of using a template image for a thumbnail, making it transparent and overlaying it on a top of resized original image.  The code in the article is using available in .NET GDI+ methods like Image.GetThumbnailImage, Bitmap.MakeTransparent, etc… So I asked myself a question: “Is it still possible to implement a similar technique in Compact Framework?” Aside from saving the resulting image to a file I couldn’t see any problems in doing that and in 15 minutes I had this code…

 

The simplified implementation of the GetThumbnailImage goes first:

 

public static Bitmap GetThumbnailImage(Bitmap image, int height, int width)

{

    Bitmap bmp = new Bitmap(height, width);

    //create temp Graphics

    Graphics gx = Graphics.FromImage(bmp);

    //Resize the image

    gx.DrawImage(image, new Rectangle(0, 0, height, width), new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);

    //don't forget the dispose

    gx.Dispose();

    return bmp;

}

 

Now the CF version of the code from the article:

 

int thumbnailWidth = 38;

int thumbnailHeight = 52;

 

//Load original bitmap first

Bitmap orig = new Bitmap(@"\Program Files\ThumbnailProj\today.PNG");

//Create a thumbnailed version

Bitmap thumbOrig = GetThumbnailImage(orig, thumbnailWidth, thumbnailHeight);

//Load a template image

Bitmap thumbTemp = new Bitmap(@"\Program iles\ThumbnailProj\template_portrait.gif");

//Create new blank bitmap

Bitmap thumbnailBitmap = new Bitmap(thumbnailWidth + 7, thumbnailHeight + 7);

                 

Graphics gx = Graphics.FromImage(thumbnailBitmap);

gx.Clear(Color.White);

//Draw a thumbnailed image version  first

gx.DrawImage(thumbOrig, 2, 2);

           

//Draw a template image on the top with transparent attributes.

//This is what we do instead of Bitmap.MakeTransparent()

ImageAttributes attr = new ImageAttributes();

attr.SetColorKey(thumbTemp.GetPixel(5, 5), thumbTemp.GetPixel(5, 5));

gx.DrawImage(thumbTemp, new Rectangle(0, 0, thumbTemp.Width, thumbTemp.Height), 0, 0, thumbTemp.Width, thumbTemp.Height, GraphicsUnit.Pixel, attr);

 

//Draw the final image on the Form/Control

Graphics gxScr = this.CreateGraphics();

gxScr.DrawImage(thumbnailBitmap, 10, 10);



You can download the whole demo project here.
1/22/2004 2:31:49 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, January 20, 2004

You probably already know about a brilliant ApplicationEx class created by Chris for OpenNETCF.  It allows getting every single Windows message that your app receives.  I 've put together a IMessageFilter implementation that would filter out the messages that are send to a particular control on your Form or to the Form itself if you wish... Here is the code that for IMessageFilter implementation:

//Delegate to raise a event to a client

public delegate void WinProc(ref Message m);

 

class WinProcFilter : IMessageFilter

{

      public WinProc WndProc;

      private IntPtr handle;

 

      public WinProcFilter(Control ctl)

      {

            //Get control's window handle           

            ctl.Capture = true;

            this.handle = GetCapture();

            ctl.Capture = false;

      }

      #region IMessageFilter Members

      public bool PreFilterMessage(ref Message m)

      {

            bool handled = false;

            if (WndProc!=null)

            {

                  if (m.HWnd == handle) //Check window handle event

                  {

                      if (WndProc!=null) 

                       WndProc(ref m); //Raise the event

                  }

            }

            return handled;

      }

      #endregion

 

      [DllImport("coredll.dll")]

      private static extern IntPtr GetCapture();

}

On the client form you would need to declare and instanciate WinProcFilter class:

//Let's get the winproc for some TextBox

WinProcFilter procFilter = new WinProcFilter(textBox1);

//Hook up to WinProc event

procFilter.WndProc+=new WinProc(textBox1_WinProc);

.....

 

private void textBox1_WinProc(ref Message m)

{

    //Do whatever you like with the messages.

}

 

1/20/2004 5:02:04 PM (GMT Standard Time, UTC+00:00)  #     | 
 Tuesday, January 13, 2004

You've probably noticed that the "Home" key press is not possible to catch in either of the Key event handles of the Form or Control. The solution to that will be to use the RegisterHotKey API in conjunction with MessageWindow:

using System;

using Microsoft.WindowsCE.Forms;

using System.Windows.Forms;

using System.Runtime.InteropServices;

public class MessageWin : MessageWindow

{

    //event for client

    public System.EventHandler HomeKeyPress;

 

    public MessageWin()

    {

       //register to listen for home key

       RegisterHotKey(this.Hwnd, VK_LWIN, 8, VK_LWIN);

    }

  

    ~MessageWin()

    {

       Unregister();

    }

 

    protected override void WndProc(ref Message m)

    {

       if (m.Msg == WM_HOTKEY)

       {

          if ((int)m.WParam == VK_LWIN)

             //Raise event

             if (HomeKeyPress!=null)

                   HomeKeyPress(this, null);

       }

       base.WndProc (ref m);

    }

 

  #region P/Invokes

    private const int VK_LWIN    =    0x5B;

    private const int WM_HOTKEY   =      0x0312;

    [DllImport("coredll.dll")]

    public static extern bool RegisterHotKey(

         IntPtr hWnd, // handle to window

         int id, // hot key identifier

         int Modifiers, // key-modifier options

         int key //virtual-key code);

    [DllImport("coredll.dll")]

    public static extern bool UnregisterHotKey(

         IntPtr      hWnd,

         int         id);

   #endregion

 }

The usage of this class should be pretty obviouse:

MessageWin  msgWin = new MessageWin();

msgWin.HomeKeyPress+=new EventHandler(msgWin_HomeKeyPress);

 

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

{

      MessageBox.Show("Home Key Pressed.");

}

 

1/13/2004 9:27:59 PM (GMT Standard Time, UTC+00:00)  #     | 
 Monday, January 12, 2004

To draw a rectangle with rounded corners is not a trivial task in Compact Framework due to lack of ability to draw arcs (Graphics.DrawArc, GraphicPaths in the big .NET, etc..). OK, so why don't we just use Graphics.DrawEllipse to draw a rounded corners instead? Definitelly we can do that, but we would have to somehow wipe out the internal arcs:

So, my solutuion to that problem is to create a poligon shape that would fill the rectangle with the required back color and would wipe out unneeded arcs:

public static void DrawRoundedRectangle(Graphics g, Pen p, Color backColor, Rectangle rc, Size size)
{
   Point[] points = new Point[8];
   
   //prepare points for poligon
   points[0].X = rc.Left + size.Width / 2;
   points[0].Y = rc.Top + 1;
   points[1].X = rc.Right - size.Width / 2;
   points[1].Y = rc.Top + 1;

   points[2].X =  rc.Right;
   points[2].Y = rc.Top + size.Height / 2;
   points[3].X =  rc.Right;
   points[3].Y = rc.Bottom - size.Height / 2;

   points[4].X =  rc.Right - size.Width / 2;
   points[4].Y = rc.Bottom;
   points[5].X =  rc.Left  + size.Width / 2;
   points[5].Y = rc.Bottom;

   points[6].X =  rc.Left + 1;
   points[6].Y = rc.Bottom - size.Height / 2;
   points[7].X =  rc.Left + 1;
   points[7].Y = rc.Top + size.Height / 2;

   //prepare brush for background
   Brush fillBrush = new SolidBrush(backColor);

   //draw side lines and circles in the corners
   g.DrawLine(p, rc.Left  + size.Width / 2, rc.Top,
    rc.Right - size.Width / 2, rc.Top);
   g.FillEllipse(fillBrush, rc.Right - size.Width, rc.Top,
    size.Width, size.Height);
   g.DrawEllipse(p, rc.Right - size.Width, rc.Top,
    size.Width, size.Height);

   g.DrawLine(p, rc.Right, rc.Top + size.Height / 2,
    rc.Right, rc.Bottom - size.Height / 2);
   g.FillEllipse(fillBrush, rc.Right - size.Width, rc.Bottom - size.Height,
    size.Width, size.Height);
   g.DrawEllipse(p, rc.Right - size.Width, rc.Bottom - size.Height,
    size.Width, size.Height);
   
   g.DrawLine(p, rc.Right - size.Width / 2, rc.Bottom,
    rc.Left  + size.Width / 2, rc.Bottom);
   g.FillEllipse(fillBrush, rc.Left, rc.Bottom - size.Height,
    size.Width, size.Height);
   g.DrawEllipse(p, rc.Left, rc.Bottom - size.Height,
    size.Width, size.Height);
   
   g.DrawLine(p, rc.Left, rc.Bottom - size.Height / 2,
    rc.Left, rc.Top + size.Height / 2);
   g.FillEllipse(fillBrush, rc.Left, rc.Top,
    size.Width, size.Height);
   g.DrawEllipse(p, rc.Left, rc.Top,
    size.Width, size.Height);
   
   //fill the background and remove the internal arcs  
      g.FillPolygon(fillBrush, points);
   //dispose the brush
   fillBrush.Dispose();
  }

And this is how you'd use this method:

Rectangle rc =  new Rectangle(10, 10, 100, 30);

DrawRoundedRectangle(e.Graphics,

new Pen(Color.Black), Color.CadetBlue, rc, new Size(8, 8));

 
1/12/2004 11:52:30 PM (GMT Standard Time, UTC+00:00)  #     | 
 Monday, January 05, 2004
There was a really interesting discussion going on Robert Scoble blog when Neil has made a comment on a " rationale behind open source."

I find this whole issue to be ridiculous in the sense that open-source project can not be taking any jobs, but on the opposite way can help to keep jobs for the working people who would save some of their productive time by not re-inventing the wheel. What's your first move when working on the project and stumbling to the task you either don't know how to do or you know would take some time to develop? Mine would be to start "googling" and trying to find out if that was already done before. And when I discover that the stuff I am doing has never been done by anyone, I'd start doing on my own. And usually the solution would end up wither in one of OpenNETCF projects or in blog or in posts on the compactframework newsgroup.

1/5/2004 1:29:58 PM (GMT Standard Time, UTC+00:00)  #    Comments [34]  | 
 Thursday, January 01, 2004

Happy New Year to everybody!

The passed year was a good year for me. I've met many cool, smart intelligent and adherent people at the different events throughout a year... OpenNETCF has emerged and started shaping up.... I am sure that the New Year will be better. As Peter rightfully mentioned "Whidbey" is coming with CF v2 and a new generation of PPC and SmartPhone devices should start appearing this year.

On the personal front I hope (fingers crossed) to get into a very big and exciting Compact Framework project with one well-known company. I'll also strive to get around to finish up quite a few different projects for CF I have laying around (I agree with Chris  - I have to learn to not start a new project before finishing the old one too...): the mentioned before simple data storage, reporting engine, popup menu, Tab control, scrollbar etc...

1/1/2004 7:05:57 PM (GMT Standard Time, UTC+00:00)  #    Comments [1]  |