Search code examples
c#windowswinforms.net-8.0screensaver

C# Custom Screensaver capturing desktop returns solid color


I am working on creating a screen saver in C# for .NET 8.0. I'm currently trying to do something similar to how the bubbles screen saver screenshots your desktop and uses that as a background.

Everything works fine, preview mode works, and running the screensaver as an .exe works too. The issue occurs when the screen saver actually runs after idling. Windows shows a solid color before running the screensaver so screenshotting just gives that.

Program class

static void ShowScreenSaver()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    foreach (Screen screen in Screen.AllScreens)
    {
        ScreenSaverForm screensaver = new(screen);
        screensaver.Show();
        screensaver.Refresh();
    }
}

ScreenSaverForm class

public Bitmap screenBitmap;

public ScreenSaverForm(Screen screen)
{
    screenBitmap = new Bitmap(screen.Bounds.Width, screen.Bounds.Height, PixelFormat.Format24bppRgb);

    Graphics captureGraphics = Graphics.FromImage(screenBitmap);

    captureGraphics.CopyFromScreen(screen.Bounds.Location, Point.Empty, screen.Bounds.Size);
    captureGraphics.Dispose();

    InitializeComponent();
    StartPosition = FormStartPosition.Manual;
    Bounds = screen.Bounds;

    timer1.Start();

    DoubleBuffered = true;

    Paint += ScreenSaverForm_Paint;
}

I tried a transparent window but the solid color just appears under it. If anyone has any insight as to how the bubbles screensaver accomplishes it or any other alternative way to screenshot under the solid color that would be appreciated!


Solution

  • When windows idling it creates separate desktop on which it draws your screen saver. This is security measurement.

        private const uint DESKTOP_READOBJECTS = 0x0001;
    
        [DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        private static extern IntPtr OpenDesktop(string lpszDesktop, int dwFlags, 
                bool fInherit, uint dwDesiredAccess);
    
    [DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle)
    
    IntPtr hdt = OpenDesktop("Default", 0, false, AccessUnlocked);
    IntPtr hWndDesktop = FindWindowEx(IntPtr.Zero, IntPtr.Zero, "Progman", null);
    

    from here, you can create Graphics and read that window.