不错呦!smile@林凯西,确保“准备文件”中的几个文件都有安装,S...您好,看了您这篇帖子觉得很有帮助。但是有个问题想请...我的修改过了怎么还被恶意注册呢 @jjjjiiii 用PJ快9年了,主要是A...PJ3啊,貌似很少有人用PJ了,现在不是WP就是z...@332347365,我当时接入时错误码没有-10...楼主,ChkValue值应为-103是什么意思呢?...大哥 你最近能看到我发的信息,请跟我联系,我有个制...
C#远程服务器屏幕截图RDP断开后句柄无效截图失败解决方法
编辑:dnawo 日期:2026-04-22
C#写了一个屏幕截图控制台应用程序,本地测试正常,传到远程服务器,RDP断开后提示句柄无效无法截图:
这是因为RDP 远程桌面关闭/最小化/断开连接后,Windows 会自动销毁远程桌面的交互式图形句柄,原生的 Graphics.CopyFromScreen() 必须依赖可见的交互式桌面句柄,没有句柄就直接报错句柄无效,改用 Windows 底层 GDI API 直接捕获显示器硬件画面可解决:
这回不出错了,但又出现新的问题,RDP断开截到的图是空白的,这是 Windows 服务器的会话隔离机制导致的,系统默认会在你关闭远程桌面后,彻底停止渲染图形界面,所有普通截图方法都会截到纯黑 / 空白画面,使用tscon命令强制把远程会话切换到服务器物理控制台,保留图形界面渲染即可:
Tips:运行命令后会断开RDP连接,无需手动关闭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();
}
}
{
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);
}
}
{
// ====================== 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
)
:: 将当前RDP会话无缝切换到服务器本地控制台
:: 保持会话后台锁定但活动,图形渲染不中断
for /f "skip=1 tokens=3" %%s in ('query user %USERNAME%') do (
%windir%\System32\tscon.exe %%s /dest:console
)
Tips:运行命令后会断开RDP连接,无需手动关闭RDP。
上一篇: .NET Framework 版本和 C# 版本对应关系表
下一篇: Android Studio Java vs Kotlin 项目文件对比
文章来自: 本站原创
Tags:
最新日志:
评论: 0 | 引用: 0 | 查看次数: 59
发表评论
请登录后再发表评论!



