locked
How to handle SIP panel window messages? RRS feed

  • Question

  • Hi Guys,

     

    I'm working on a Windows Mobile Compact Framework project where it is a requirement to let the user to move the SIP panel with the stylus/finger. On Windows CE the SIP panel has a caption area, so I thought to modify the window style using the WS_CAPTION flag is a good solution. 

    The following code does the trick and this is good so far:

    IntPtr sipHandle = FindWindowCE("SipWndClass", null);
    uint style = GetWindowLong(sipHandle, GWL_STYLE);
    style |= WS_CAPTION;
    SetWindowLong(sipHandle, GWL_STYLE, style);
    


    However there is another window connecting to the SIP panel called "SipBackDropWndClass" which must move together with the "SipWndClass" window automatically.

    That's why I used the OpenNetCF.Windows.Forms.NativeMethod class to subclass the SIP panel and listen to the window messages and catch the WM_WINDOWPOSCHANGED message to modify the size and the position of the "SipBackDropWndClass" window appropriately.

    Basically what I'm trying to do is to use the NativeWindow.AssignHandle method of the NativeWindow class to subclass the SIP panel window which works fine. But when I call the NativeWindow.ReleaseHandle I loose the access to the keyboard and only a warm boot solves the problem. The same happens when I call only the NativeWindow.AssignHandle method without releasing it later. 

    By the way the solution works perfectly, the user can move the keyboard and drop it everywhere on the screen. But when the NativeWindow.ReleaseHandle is called then the keyboard is stop working. Sometimes even the Windows Mobile is not working correctly. (Like I cannot open the FileExplorer or othere windows anymore!!??)

     

    Is there a way to catch the WM_WINDOWPOSCHANGED message of the SIP panel appropriately without these side effects?

     

    Thank you for your help in advance. I'm trying to figure this out since days, but now I got stucked and I have no idea.

    Sunday, November 27, 2011 11:26 AM

All replies

  • Basically what I would like to do is to subclass an unmanaged window from a Compact Framework C# application where the operating system is Windows Mobile 6.5.

    The approach I'm using is this. (I created "my own" NativeWindow class just for this function)

    [DllImport("coredll.dll")]
    private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, Win32WndProc newProc);
    [DllImport("coredll.dll")]
    private static extern int CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, int Msg, int wParam, int lParam);
    
    // A delegate that matches Win32 WNDPROC:
    private delegate int Win32WndProc(IntPtr hWnd, int Msg, int wParam, int lParam);
    
    // from winuser.h:
    private const int GWL_WNDPROC = -4;
    private const int WM_WINDOWPOSCHANGED= 0x47;
    
    // program variables
    private IntPtr oldWndProc = IntPtr.Zero;
    private Win32WndProc newWndProc = null;
    
    void SubclassHWnd(IntPtr hWnd)
    {
       // hWnd is the window you want to subclass..., create a new 
       // delegate for the new wndproc
       newWndProc = new Win32WndProc(MyWndProc);
       // subclass
       oldWndProc = SetWindowLong(hWnd, GWL_WNDPROC, newWndProc);
    }
    
    void UnSublcassHwnd(IntPtr hWnd)
    {
       SetWindowLong(hWnd, GWL_WNDPROC, oldWndProc);
    }
    
    private int MyWndProc(IntPtr hWnd, int Msg, int wParam, int lParam)
    {
       switch(Msg)
       {
          case WM_WINDOWPOSCHANGED:
             // Do the appropriate action here and move the "SipBackDropWndClass".
             return 0;
    
          default:
             break;
       }
    
       return CallWindowProc(oldWndProc, hWnd, Msg, wParam, lParam);
    }
    

    The subclass and unsubclass methods works fine. When I display the keyboard I user the subclass method and I can successfully catch the WM_WINDOWPOSCHANGED message. The unsubclass seems works too. However when I exit from the application the keyboard is lost. It is not possible to access it anymore.

    Both the SipWndClass and SipBackDropWndClass windows point to null. It seems the windows are not exists.

    How is this possible?

    Only a warm boot solves the problem.

    I can send the full test application to anyone who would like to help. The application works fine, but just until I exit.

    Monday, November 28, 2011 1:29 PM
  • Hello,

     

    As far as I know, the SetWindowLong will inject your codes into another Windows. Therefore, if your application exited, the codes which you want to inject was lost, so the other Windows will crash.

     

    I suggest you should keep the old WndProc in your codes. Before your application exit, you should restore the old WndProc, so that the other Windows will work fine.

     

    Best regards,

    Jesse


    Jesse Jiang [MSFT]
    MSDN Community Support | Feedback to us
    Tuesday, November 29, 2011 7:14 AM
  • Hi Jesse,

     

    Thank you for your answer. I unsubclass the unmanaged SIP panel each time when the user hides the keyboard and I restore the original WndProc.

    Up to now I was testing my sample application in debug mode to be able the keep track what's happening. Now I realized that when I exit from the application the keyboard is still accessible, but when I start the app again and try to display it I lost it forever. (only a warm boot solves the problem).

    Here is my implementation of the NativeWindow class that I use to inherit from. Actually this does the same like the OpenNetCF.Windows.Forms.NativeWindow class.

     public class NativeWindowEx
        {
            [DllImport("coredll.dll")]
            private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
            [DllImport("coredll.dll", SetLastError = true)]
            private static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);
            [DllImport("coredll.dll")]
            static extern IntPtr DefWindowProc(IntPtr hWnd, int uMsg, IntPtr wParam, IntPtr lParam);
            [DllImport("coredll.dll")]
            static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
            [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            static extern bool DestroyWindow(IntPtr hwnd);
            [DllImport("coredll.dll", SetLastError = true)]
            static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, WndProcDelegate newProc);
            [DllImport("coredll.dll", SetLastError = true)]
            static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr newProc);
    
            public delegate IntPtr WndProcDelegate(
               IntPtr hWnd,
               uint msg,
               IntPtr wParam,
               IntPtr lParam
            );
    
            #region fields
    
            private int GWL_WNDPROC = -4;
            private IntPtr handle;
            private IntPtr defWindowProc;
            private WndProcDelegate windowProc;
            private bool ownHandle;
    
            #endregion // fields
    
            #region contructors
    
            public NativeWindowEx()
            {
                handle = IntPtr.Zero;
                ownHandle = true;
            }
    
            #endregion // contructors
    
            #region properties
    
            public IntPtr Handle
            {
                get
                {
                    return handle;
                }
            }
    
            #endregion // properties
    
            #region methods
    
            /// <summary> 
            /// Assigns a handle to this window.    
            /// </summary> 
            /// <param name="handle">The handle to assign to this window.</param> 
            public void AssignHandle(IntPtr handle)
            {
                if (this.handle != IntPtr.Zero)
                {
                    ReleaseHandle();
                }
    
                this.handle = handle;
                this.ownHandle = false;
                Subclass();
                OnHandleChange();
            }
    
            /// <summary> 
            /// Releases the handle associated with this window.  
            /// </summary> 
            public void ReleaseHandle()
            {
                if (this.handle == IntPtr.Zero)
                {
                    return;
                }
    
                this.UnSubclass(false);
    
                this.handle = IntPtr.Zero;
    
                this.defWindowProc = IntPtr.Zero;
                this.windowProc = null;
    
                if (ownHandle)
                {
                    DestroyWindow(this.handle);
                }
    
                this.handle = IntPtr.Zero;
            }
    
            /// <summary> 
            /// Invokes the default window procedure associated with this window.    
            /// </summary> 
            /// <param name="m">A System.Windows.Forms.Message that is associated with the current Windows message.</param> 
            protected virtual void WndProc(ref Message m)
            {
                this.DefWndProc(ref m);
            }
    
            /// <summary> 
            /// Invokes the default window procedure associated with this window. It is an error to call this method when the System.Windows.Forms.NativeWindow.Handle property is 0.   
            /// </summary> 
            /// <param name="m">A System.Windows.Forms.Message that is associated with the current Windows message.</param> 
            public void DefWndProc(ref Message m)
            {
                if (this.defWindowProc == IntPtr.Zero)
                {
                    m.Result = DefWindowProc(m.HWnd, m.Msg, m.WParam, m.LParam);
                }
                else
                {
                    m.Result = CallWindowProc(this.defWindowProc, m.HWnd, (uint)m.Msg, m.WParam, m.LParam);
                }
            }
    
            /// <summary> 
            /// Destroys the window and its handle.   
            /// </summary> 
            public virtual void DestroyHandle()
            {
                ReleaseHandle();
            }
    
            protected virtual void OnHandleChange()
            {
    
            }
    
            #endregion // methods
    
            #region static methods
    
            /// <summary> 
            /// Retrieves the window associated with the specified handle.   
            /// </summary> 
            /// <param name="handle">A handle to a window.</param> 
            /// <returns>The System.Windows.Forms.NativeWindow associated with the specified handle. This method returns null when the handle does not have an associated window.</returns> 
            public static NativeWindowEx FromHandle(IntPtr handle)
            {
                NativeWindowEx window = new NativeWindowEx();
    
                window.AssignHandle(handle);
    
                return window;
            }
    
            #endregion // static methods
    
            #region helper
    
            private IntPtr Callback(IntPtr hWnd, uint msg, IntPtr wparam, IntPtr lparam)
            {
                Message message = Message.Create(hWnd, (int)msg, wparam, lparam);
                try
                {
                    this.WndProc(ref message);
                }
                catch (Exception)
                {
                    throw;
                }
                if (msg == 130)
                {
                    this.ReleaseHandle();
                }
                return message.Result;
            }
    
    
            private void Subclass()
            {
                if (this.handle != IntPtr.Zero)
                {
                    this.defWindowProc = GetWindowLong(this.handle, GWL_WNDPROC);
                    windowProc = new WndProcDelegate(Callback);
    
                    //int success = SetWindowLong(handle, GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(windowProc));
                    SetWindowLong(handle, GWL_WNDPROC, windowProc);
                }
    
            }
    
            private void UnSubclass(bool finalizing)
            {
                int success = SetWindowLong(handle, GWL_WNDPROC, this.defWindowProc);
            }
    
            #endregion // helper
    
    
            ~NativeWindowEx()
            {
                if (handle != IntPtr.Zero)
                {
                    ReleaseHandle();
                }
            }
        } 
    


    Before I display the keyboard I call the AssignHandle method with the appropriate window handle and it is hidden I call the ReleaseHandle method.

    It works for the first shot, but when I start the application second time it doesn't do anything. After calling the AssignHandle method the "SipWndClass" and "SipBackDropWndClass" windows are gone and the handle is null and the FindWindow("SipWndClass", null) statement returns null too.

    Do you have idea why it works for the first time when I start the application and why it doesn't when I restart it again?

     

    Thank you for your help in advance!

    Tuesday, November 29, 2011 10:06 AM
  • I realized it is very easy to reproduce the problem. Using the NativeWindowEx class that I described earlier, it is easy to inherit from and override the WndProc method to handle the appropriate window messages.

    Using the following two methods are enough to see what I'm talking about:

    InputPanelSubClass subClass = new InputPanelSubClass();
    
    public void Show()
    {
        IntPtr sipHandle = FindWindow("SipWndClass", null);
        subClass.AssignHandle(sipHandle);
        ShowWindow(sipHandle, 1);
    }
    
    public void Hide()
    {
        subClass.ReleaseHandle();
        IntPtr h = FindWindow("SipWndClass", null);
        ShowWindow(h, 0);
    }
    


    Here is the code of the InputPanelSubClass:

    public class InputPanelSubclass : NativeWindowEx
        {
            #region P/Invoke
    
            [DllImport("coredll.dll", SetLastError = true)]
            private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    
            [DllImport("coredll.dll", SetLastError = true)]
            private static extern IntPtr MoveWindow(IntPtr hwnd, int x, int y, int w, int l, int repaint);
    
    
            private struct WindowPos
            {
                public IntPtr hwnd;
                public IntPtr hwndInsertAfter;
                public int x;
                public int y;
                public int cx;
                public int cy;
                public uint flags;
            };
    
            #endregion
    
            public InputPanelSubclass() { }
    
            protected override void WndProc(ref Microsoft.WindowsCE.Forms.Message m)
            {
                if (m.Msg == (int)0x47)
                {
                    WindowPos pos = (WindowPos)(Marshal.PtrToStructure(m.LParam, typeof(WindowPos)));
                    IntPtr h = FindWindow("SipBackDropWndClass", null);
                    MoveWindow(h, pos.x, pos.y, pos.cx, pos.cy, 1);
                    return;
                }
                base.WndProc(ref m);
            }
        }
    


    Everything works for the first time, but when you restart the app, the keyboard is not working any longer. Other applications cannot use the keyboard too. Only a warm boot gives back the keyboard panel.

    Maybe this helps and someone smarter than I can tell me what's wrong and why it works for the first time and not for the second time.

    Thank you in advance.

    Tuesday, November 29, 2011 12:27 PM