views:

104

answers:

1

Ok so Google is not my friend tonight...

I have a screen saver, CC.Votd (Full source on Codeplex), and I've just started to implement the preview mode (/p argument) which is working ok. When it's in preview mode I make my form a child to the little computer monitor window and it draws in there.

This works fine and my application exits if the display properties dialog goes away.

The issue is that if I select my screen saver from the list and then select a different screen saver mine continues to run and draws over the newly selected screen saver's preview.

So how do I know when a different screen saver is selected and mine should close?


Edit: For Anon, here's the code I'm using to make my form a child of the preview window:

P/invokes:

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

[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

[DllImport("user32.dll", SetLastError = true)]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);

[DllImport("user32.dll")]
static extern bool GetClientRect(IntPtr hWnd, out Rectangle lpRect);

The code:

SetParent(Handle, _PreviewHandle);
SetWindowLong(Handle, -16, new IntPtr(GetWindowLong(Handle, -16) | 0x40000000));

Rectangle parentRectangle;
GetClientRect(_PreviewHandle, out parentRectangle);
Size = parentRectangle.Size;

Location = new Point(0, 0);

Complete form code: http://ccvotd.codeplex.com/SourceControl/changeset/view/40085#862458


Forgot to mention that I tried using IsWindowVisible() and that didn't work since the preview window is still visible and has the same handle as when my screen saver was selected.

Edit: Before I added the SetParent() and associated calls my application would continue to run after the display dialog was closed so I think that part is working and something different happens when the user selects a different screen saver.


As John K suggested I've been looking at my form with Spy++. I never see the WS_CHILD style applied. However all my debugging suggests it should be. I modified the code to:

long style = GetWindowLong(Handle, -16);
System.Diagnostics.Trace.WriteLine("Original Style: " + style);
style &= ~0x800000000;
style |= 0x40000000;
System.Diagnostics.Trace.WriteLine("Adjusted Style: " + style);

SetWindowLong(Handle, -16, new IntPtr(style));
System.Diagnostics.Trace.WriteLine("After Set Style: " + GetWindowLong(Handle, -16));
SetParent(Handle, _PreviewHandle);
System.Diagnostics.Trace.WriteLine("After Set Parent: " + GetWindowLong(Handle, -16));

And the style is the same on the last three traces, two of which should be getting the value from the form itself. Going to research my native API calls and clean up their declarations to see what I can figure out.

Thanks for all the help so far!


Solution: The problem ended up being that I was setting several properties of the form that resulted in the underlying .NET control overwriting my new styles. So changing:

SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);

Capture = true;

if (!_IsPreview)
{
    // Removed ...
}
else
{
    SetWindowLong(Handle, -16, new IntPtr(GetWindowLong(Handle, -16) | 0x40000000));
    SetParent(Handle, _PreviewHandle);

    Rectangle parentRectangle;
    GetClientRect(_PreviewHandle, out parentRectangle);
    Size = parentRectangle.Size;

    Location = new Point(0, 0);
}

ShowInTaskbar = false;
DoubleBuffered = true;
BackgroundImageLayout = ImageLayout.Stretch;

To:

SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);

BackgroundImageLayout = ImageLayout.Stretch;
Capture = true;
DoubleBuffered = true;
ShowInTaskbar = false;

if (!_IsPreview)
{
    // Removed ...
}
else
{
    SetWindowLong(Handle, -16, new IntPtr(GetWindowLong(Handle, -16) | 0x40000000));
    SetParent(Handle, _PreviewHandle);

    Rectangle parentRectangle;
    GetClientRect(_PreviewHandle, out parentRectangle);
    Size = parentRectangle.Size;

    Location = new Point(0, 0);
}

Fixed the problem. Simple mistake :-)


The correct way to solve it... override CreateParams:

protected override CreateParams CreateParams
{
    get
    {
        CreateParams createParams = base.CreateParams;

        if (!DesignMode && _IsPreview)
        {
            createParams.Style |= 0x40000000;
        }

        return createParams;
    }
}
+2  A: 

Once apon a time trying to change the WS_CHILD style of a window after it was already created would just quietly fail. I think they changed that in current versions of windows, but to be sure, you should really be creating your preview form as a child window from the start.

I have a hunch that your window isn't ending up a child window of the preview. you could try this.

SetParent(Handle, _PreviewHandle);
SetWindowLong(Handle, -16, new IntPtr(GetWindowLong(Handle, -16) | 0x40000000));
SetParent(Handle, _PreviewHandle);

SetParent after you change your window style to WS_CHILD.

Also, you may not have the WS_POPUP style on your form, but if you do, you want to remove it.

int style = GetWindowLong(Handle, -16);
style &= ~0x800000000;
style |= 0x40000000;
SetWindowLong(Handle, -16, new IntPtr(style));

What's happening here is that SetParent sets the parent of child windows, but it sets the owner of WS_POPUP and WS_OVERLAPPED windows.

John Knoeller
@John: Thanks again for pointing me in the right direction.
Cory Charlton