protected override void WndProc(ref Message message) {
      switch (message.Msg) {
        case WM_COMMAND: {
            // need to dispatch the message for the context menu
            if (message.LParam == IntPtr.Zero)
              commandDispatch.Invoke(null, new object[] { 
              message.WParam.ToInt32() & 0xFFFF });
          } break;
        case WM_NCHITTEST: {
            message.Result = (IntPtr)HitResult.Caption;
            if (HitTest != null) {
              Point p = new Point(
                Macros.GET_X_LPARAM(message.LParam) - location.X,
                Macros.GET_Y_LPARAM(message.LParam) - location.Y
              );
              HitTestEventArgs e = new HitTestEventArgs(p, HitResult.Caption);
              HitTest(this, e);
              message.Result = (IntPtr)e.HitResult;
            }
          } break;
        case WM_NCLBUTTONDBLCLK: {
            if (MouseDoubleClick != null) {
              MouseDoubleClick(this, new MouseEventArgs(MouseButtons.Left, 2,
                Macros.GET_X_LPARAM(message.LParam) - location.X,
                Macros.GET_Y_LPARAM(message.LParam) - location.Y, 0));
            }
            message.Result = IntPtr.Zero;
          } break;
        case WM_NCRBUTTONDOWN: {
            message.Result = IntPtr.Zero;
          } break;
        case WM_NCRBUTTONUP: {
            if (contextMenu != null)
              ShowContextMenu(new Point(
                Macros.GET_X_LPARAM(message.LParam),
                Macros.GET_Y_LPARAM(message.LParam)
              ));
            message.Result = IntPtr.Zero;
          } break;
        case WM_WINDOWPOSCHANGING: {
            WindowPos wp = (WindowPos)Marshal.PtrToStructure(
              message.LParam, typeof(WindowPos));
            
            if (!lockPositionAndSize) {
              // prevent the window from leaving the screen
              if ((wp.flags & SWP_NOMOVE) == 0) {
                Rectangle rect = Screen.GetWorkingArea(
                  new Rectangle(wp.x, wp.y, wp.cx, wp.cy));
                const int margin = 16;
                wp.x = Math.Max(wp.x, rect.Left - wp.cx + margin);
                wp.x = Math.Min(wp.x, rect.Right - margin);
                wp.y = Math.Max(wp.y, rect.Top - wp.cy + margin);
                wp.y = Math.Min(wp.y, rect.Bottom - margin);
              }

              // update location and fire event
              if ((wp.flags & SWP_NOMOVE) == 0) {
                if (location.X != wp.x || location.Y != wp.y) {
                  location = new Point(wp.x, wp.y);
                  if (LocationChanged != null)
                    LocationChanged(this, EventArgs.Empty);
                }
              }

              // update size and fire event
              if ((wp.flags & SWP_NOSIZE) == 0) {
                if (size.Width != wp.cx || size.Height != wp.cy) {
                  size = new Size(wp.cx, wp.cy);
                  if (SizeChanged != null)
                    SizeChanged(this, EventArgs.Empty);
                }
              } 

              // update the size of the layered window
              if ((wp.flags & SWP_NOSIZE) == 0) {
                NativeMethods.UpdateLayeredWindow(Handle, IntPtr.Zero,
                  IntPtr.Zero, ref size, IntPtr.Zero, IntPtr.Zero, 0,
                  IntPtr.Zero, 0);                
              }

              // update the position of the layered window
              if ((wp.flags & SWP_NOMOVE) == 0) {
                NativeMethods.SetWindowPos(Handle, IntPtr.Zero, 
                  location.X, location.Y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | 
                  SWP_NOZORDER | SWP_NOSENDCHANGING);
              }
            }
            
            // do not forward any move or size messages
            wp.flags |= SWP_NOSIZE | SWP_NOMOVE;

            // suppress any frame changed events
            wp.flags &= ~SWP_FRAMECHANGED;

            Marshal.StructureToPtr(wp, message.LParam, false);                      
            message.Result = IntPtr.Zero;
          } break;
        default: {
            base.WndProc(ref message);
          } break;
      }      
    }