1
votes

Using code I found on CodeProject, I've created a screen saver. The following form is the tiny little form that I show in the Control Panel when the user chooses my screen saver.

Everything seems to work fine; the form correctly resizes and draws (it's blank) in the correct place in the control panel, moves with the CP, etc. But when the Control Panel closes (or replaces my form with another screen saver's mini-preview), my app does not die. It just hangs out in memory.

My form gets no form closed/closing messages, or visibility changes, etc. Am I not setting parenthood correctly here?

Here is the relevant code. All of the Imported WinAPI calls return the expected values, and GetLastError always returns zero, so I think this is not the problem...

    private void miniControlPanelForm_Load(object sender, EventArgs e)
    {

        // note that iphWnd is a class variable, passed to us by windows

        // set our window style to WS_CHILD, so that our window is
        // destroyed when parent window is destroyed.

        // get the current window style, but with WS_CHILD set
        IntPtr ip = new IntPtr();
        int index = (int)NativeMethods.WindowLongFlags.GWL_STYLE | 0x40000000;   
        ip = NativeMethods.GetWindowLongPtr(this.Handle, index);
        int error = System.Runtime.InteropServices.Marshal.GetLastWin32Error();

        // set that value as our current Style
        object ohRef = new object();
        HandleRef hRef = new HandleRef(ohRef, this.Handle);
        IntPtr ip2 = new IntPtr();
        int index2 = (int)NativeMethods.WindowLongFlags.GWL_STYLE;
        ip2 = NativeMethods.SetWindowLongPtr(hRef, index2, ip);
        error = System.Runtime.InteropServices.Marshal.GetLastWin32Error();

        // set the passed preview window as the parent of this window
        IntPtr newOldParent = NativeMethods.SetParent(this.Handle, iphWnd);
        error = System.Runtime.InteropServices.Marshal.GetLastWin32Error();

        //set our window's size to the size of our window's new parent
        Rectangle ParentRect = new Rectangle();
        NativeMethods.GetClientRect(iphWnd, ref ParentRect);
        this.Size = ParentRect.Size;

        //set our location at (0, 0)
        this.Location = new Point(0, 0);
    }

I have Application.Exit in the various "form is closing" event handlers, but they never get called...

1
Related (although not exceedingly helpful): stackoverflow.com/questions/21967757/…Blorgbeard
Related (and actually quite helpful): stackoverflow.com/questions/12203577/…Blorgbeard

1 Answers

1
votes

All of these problems go away if you properly make the form a child of the control panel window. Here is how I do it now, and it works in all cases.

In the form, add this, which forces the form's window style to WS_CHILD at creation time:

    /// <summary>
    /// Override CreateParams property so we can add "WS_CHILD" to
    /// the Style each time it is queried during window creation.
    /// </summary>
    protected override CreateParams CreateParams
    {
        get
        {
            // get the base params and modify them
            CreateParams cp = base.CreateParams;
            cp.Style |= NativeMethods.WindowStyles.WS_CHILD;
            return cp;
        }
    }

In the code which receives the hWnd for the control panel and creates the form, use SetParent to make the form a child of the control panel:

    /// <summary>
    /// Show the form in the little control panel preview window.
    /// </summary>
    /// <param name="hWnd">hwnd passed to us at launch by windows</param>
    static void ShowMiniPreview(IntPtr hWnd)
    {
        if (NativeMethods.IsWindow(hWnd))
        {
            miniControlPanelForm preview = new miniControlPanelForm(hWnd);
            IntPtr newParent = NativeMethods.SetParent(preview.Handle, hWnd);

            // Set the size of the form to the size of the parent window (using the passed hWnd).
            System.Drawing.Rectangle ParentRect = new System.Drawing.Rectangle();
            bool fSuccess = NativeMethods.GetClientRect(hWnd, ref ParentRect);

            // Set our size to new rect and location at (0, 0)
            preview.Size = ParentRect.Size;
            preview.Location = new System.Drawing.Point(0, 0);

            // Show the form
            preview.Show();

            // and run it
            Application.Run(preview);
        }
    }

Note that "NativeMethods" is my class with various Win32 methods and constants declared as PInvokes:

    [DllImport("user32.dll", SetLastError = true)]
    internal static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

    public static class WindowStyles
    {
        public static readonly Int32
        WS_CHILD = 0x40000000;
    }

    [DllImport("user32.dll", SetLastError = true)]
    internal static extern bool GetClientRect(IntPtr hWnd, ref Rectangle rect);