You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WinAPI新建桌面截图失败:CopyFromScreen报无效句柄错误排查

新建桌面下截图失败与切换桌面空白的问题分析

问题描述

已检查所有API返回值,除截图方法执行环节外无其他失败情况。由于调用进程运行在新建桌面的线程上,graphics.CopyFromScreen步骤出现“无效句柄”错误;尝试调用SwitchDesktop切换桌面后,屏幕显示空白。请问该问题的原因是什么?

相关代码

using System.Diagnostics;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows.Media.Imaging;

internal class Program
{
    private static void Main(string[] args)
    {
        ProcessStartInfo ps = new ProcessStartInfo()
        {
            UseShellExecute = false,
            RedirectStandardInput = true,
            FileName = "notepad.exe"
        };

        IntPtr desktopHandle = CreateDesktopExW("abcdesktop", null, IntPtr.Zero, 0, access, IntPtr.Zero, 1000, IntPtr.Zero);

        if(desktopHandle == IntPtr.Zero)
        {
            MessageBox.Show(GetLastError().ToString());
            return;
        }

        if (!SetThreadDesktop(desktopHandle))
        {
            MessageBox.Show(GetLastError().ToString());
            return;
        }

        using (Process notepad = Process.Start(ps))
        {
            notepad.StandardInput.Write("Hi");
            TakeScreenShot();
        }

        Task.Delay(10000).Wait();

        if (!CloseDesktop(desktopHandle))
        {
            MessageBox.Show(GetLastError().ToString());
            return;
        }

        if (!SetThreadDesktop(IntPtr.Zero))
        {
            MessageBox.Show(GetLastError().ToString());
            return;
        }

        MessageBox.Show("Test succeeded!");
    }

    static void TakeScreenShot()
    {
        // Get the device context of the entire screen
        IntPtr hScreenDC = GetDC(IntPtr.Zero);

        // Define the width and height of the screenshot
        int width = Screen.PrimaryScreen.Bounds.Width;
        int height = Screen.PrimaryScreen.Bounds.Height;

        // Create a compatible bitmap
        using (Bitmap bitmap = new Bitmap(width, height))
        {
            // Create a Graphics object associated with the screen device context
            using (Graphics graphics = Graphics.FromImage(bitmap))
            {
                // Capture the screen
                graphics.CopyFromScreen(0, 0, 0, 0, new Size(width, height));
            }

            // Save the captured image
            bitmap.Save(Path.Combine(Directory.GetCurrentDirectory(), "ScreenShot.png"), ImageFormat.Png);
        }

        // Release the device context
        ReleaseDC(IntPtr.Zero, hScreenDC);
    }

    #region dllImports

    [DllImport("user32.dll")]
    static extern IntPtr CreateDesktopExW(
        [MarshalAs(UnmanagedType.LPWStr)] string lpszDesktop,
        [MarshalAs(UnmanagedType.LPWStr)] string lpszDevice,
        IntPtr pDevmode,
        UInt32 dwFlags,
        UInt32 dwDesiredAccess,
        IntPtr lpsa,
        UInt32 ulHeapSize,
        IntPtr pvoid
        );

    static UInt32 access = (UInt32)(0x0004L | 0x0002L | 0x0040L | 0x0008L | 0x0020L | 0x0010L | 0x0001L | 0x0100L | 0x0080L);

    [DllImport("user32.dll")]
    static extern bool CloseDesktop(IntPtr hDesktop);

    [DllImport("kernel32.dll")]
    public static extern uint GetLastError();

    [DllImport("user32.dll")]
    static extern bool SetThreadDesktop(IntPtr hDesktop);

    [DllImport("user32.dll")]
    public static extern IntPtr GetDC(IntPtr hWnd);

    [DllImport("user32.dll")]
    public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

    [DllImport("user32.dll")]
    static extern bool SwitchDesktop(IntPtr hDesktop);

    #endregion
}

问题原因

截图失败(无效句柄)

  • 线程切换到新建桌面后,GetDC(IntPtr.Zero)获取的是当前线程绑定桌面的设备上下文(DC),但这个新建桌面并未被激活显示,其DC处于未初始化的无效状态,导致graphics.CopyFromScreen无法完成像素拷贝,触发“无效句柄”错误。
  • Screen.PrimaryScreen获取的是物理屏幕尺寸,但未激活的桌面没有对应的显示输出上下文,进一步导致拷贝操作的目标资源无效。

SwitchDesktop后屏幕空白

  • 新建的abcdesktop没有加载Windows shell进程(explorer.exe),而shell负责提供桌面背景、任务栏等基础UI元素,没有这些组件,桌面自然显示为空。
  • 虽然在新桌面启动了notepad,但未激活的桌面中进程不会立即绘制窗口,即使切换过去,也需要等待进程完成窗口初始化才能看到内容,直接切换会因窗口未渲染而显示空白。

解决方向

  • 截图前必须调用SwitchDesktop(desktopHandle)激活新桌面,确保其DC处于有效状态,再执行截图操作。
  • 激活桌面后,延长等待时间(比如调整Task.Delay时长)或通过API监听notepad窗口创建完成,确保窗口已绘制后再截图。
  • 如果需要完整桌面UI,在新桌面启动explorer.exe进程加载shell组件。

内容的提问来源于stack exchange,提问作者Alexander Jansson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 08:19:55