C#远程服务器屏幕截图RDP断开后句柄无效截图失败解决方法

C#写了一个屏幕截图控制台应用程序,本地测试正常,传到远程服务器,RDP断开后提示句柄无效无法截图:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("5秒后自动截图...\n");
        Thread.Sleep(5000);

        try
        {
            string savePath = AppDomain.CurrentDomain.BaseDirectory;
            string fileName = $"ScreenShot_{DateTime.Now:yyyyMMdd_HHmmss}.png";
            string fullPath = Path.Combine(savePath, fileName);

            Rectangle screenSize = Screen.PrimaryScreen.Bounds;
            using (Bitmap bitmap = new Bitmap(screenSize.Width, screenSize.Height))
            {
                using (Graphics graphics = Graphics.FromImage(bitmap))
                {
                    graphics.CopyFromScreen(screenSize.X, screenSize.Y, 0, 0, screenSize.Size);
                }
                bitmap.Save(fullPath, System.Drawing.Imaging.ImageFormat.Png);
            }
            Console.WriteLine($"✅ 截图成功!");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"❌ 截图失败:{ex.Message}");
        }

        Console.ReadKey();
    }
}

这是因为RDP 远程桌面关闭/最小化/断开连接后,Windows 会自动销毁远程桌面的交互式图形句柄,原生的 Graphics.CopyFromScreen() 必须依赖可见的交互式桌面句柄,没有句柄就直接报错句柄无效,改用 Windows 底层 GDI API 直接捕获显示器硬件画面可解决:

class Program
{
    // ====================== Windows底层API核心(解决RDP句柄问题)======================
    [DllImport("gdi32.dll")]
    private static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop);
    [DllImport("user32.dll")]
    private static extern IntPtr GetDesktopWindow();
    [DllImport("user32.dll")]
    private static extern IntPtr GetWindowDC(IntPtr hWnd);
    [DllImport("user32.dll")]
    private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

    static void Main(string[] args)
    {
        Console.WriteLine("5秒后自动截图...\n");
        Thread.Sleep(5000);

        try
        {
            string saveDir = AppDomain.CurrentDomain.BaseDirectory;
            string fileName = $"ScreenShot_{DateTime.Now:yyyyMMdd_HHmmss}.png";
            string savePath = Path.Combine(saveDir, fileName);
            CaptureScreen(savePath);
            Console.WriteLine("✅ 截图成功!");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"❌ 失败:{ex.Message}");
        }

        Console.ReadKey();
    }

    /// <summary>
    /// 底层GDI截图(解决RDP句柄无效问题)
    /// </summary>
    private static void CaptureScreen(string savePath)
    {
        // 获取整个桌面窗口句柄
        IntPtr desktopWnd = GetDesktopWindow();
        IntPtr dc = GetWindowDC(desktopWnd);

        // 获取屏幕分辨率
        int width = Screen.PrimaryScreen.Bounds.Width;
        int height = Screen.PrimaryScreen.Bounds.Height;

        using (Bitmap bmp = new Bitmap(width, height))
        using (Graphics g = Graphics.FromImage(bmp))
        {
            IntPtr hdc = g.GetHdc();
            // 直接硬件级拷贝屏幕(不依赖交互式句柄)
            BitBlt(hdc, 0, 0, width, height, dc, 0, 0, 0x00CC0020);
            g.ReleaseHdc(hdc);
            bmp.Save(savePath, ImageFormat.Png);
        }

        ReleaseDC(desktopWnd, dc);
    }
}

这回不出错了,但又出现新的问题,RDP断开截到的图是空白的,这是 Windows 服务器的会话隔离机制导致的,系统默认会在你关闭远程桌面后,彻底停止渲染图形界面,所有普通截图方法都会截到纯黑 / 空白画面,使用tscon命令强制把远程会话切换到服务器物理控制台,保留图形界面渲染即可:

@echo off
:: 将当前RDP会话无缝切换到服务器本地控制台
:: 保持会话后台锁定但活动,图形渲染不中断
for /f "skip=1 tokens=3" %%s in ('query user %USERNAME%') do (
  %windir%\System32\tscon.exe %%s /dest:console
)

Tips:运行命令后会断开RDP连接,无需手动关闭RDP。

评论: 0 | 引用: 0 | 查看次数: 59
发表评论
登录后再发表评论!