.NET Framework 雖提供豐富繪圖類別,但全螢幕擷取仍需呼叫 Windows GDI API (gdi32.dll) 取得裝置內容 (DC) 並執行位元區塊傳輸。本文示範使用 P/Invoke 宣告 CreateDC、BitBlt,配合 Graphics/Bitmap 實作螢幕截圖儲存為 JPEG/PNG 等格式。
using System.Runtime.InteropServices;
public static class Gdi32
{
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, IntPtr lpInitData);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight,
IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
[DllImport("gdi32.dll", ExactSpelling = true)]
public static extern int DeleteDC(IntPtr hdc);
// 光柵作業代碼
public const uint SRCCOPY = 0x00CC0020; // 來源直接複製
public const uint CAPTUREBLT = 0x40000000; // 包含分層視窗
}
CreateDC("DISPLAY", null, null, IntPtr.Zero) 取得螢幕 DC。Graphics.FromHdc(screenDc) 建立螢幕 Graphics 物件。Bitmap (寬、高、Graphics)。Graphics.FromImage(bitmap) 建立目標 Graphics。Graphics.GetHdc() 取得 DC 控制代碼。BitBlt(destDc, 0, 0, width, height, srcDc, 0, 0, SRCCOPY | CAPTUREBLT)。Graphics.ReleaseHdc() 釋放 DC。Bitmap.Save(path, ImageFormat.Jpeg/Png/Bmp) 儲存。
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
class ScreenCapture
{
// P/Invoke 宣告 (同上)
// ...
static void Main()
{
// 1. 取得螢幕 DC
IntPtr screenDc = Gdi32.CreateDC("DISPLAY", null, null, IntPtr.Zero);
if (screenDc == IntPtr.Zero)
{
Console.Error.WriteLine("CreateDC 失敗");
return;
}
try
{
// 2. 螢幕 Graphics
using (Graphics gSrc = Graphics.FromHdc(screenDc))
{
// 3. 建立符合螢幕大小的 Bitmap
int width = Screen.PrimaryScreen.Bounds.Width;
int height = Screen.PrimaryScreen.Bounds.Height;
using (Bitmap bmp = new Bitmap(width, height, gSrc))
{
// 4. 目標 Graphics
using (Graphics gDest = Graphics.FromImage(bmp))
{
// 5. 取得 DC 控制代碼
IntPtr srcHdc = gSrc.GetHdc();
IntPtr destHdc = gDest.GetHdc();
try
{
// 6. 位元區塊傳輸
bool ok = Gdi32.BitBlt(destHdc, 0, 0, width, height, srcHdc, 0, 0,
Gdi32.SRCCOPY | Gdi32.CAPTUREBLT);
if (!ok)
{
int err = Marshal.GetLastWin32Error();
Console.Error.WriteLine($"BitBlt 失敗, Error: {err}");
}
}
finally
{
// 7. 釋放 DC (極重要,否則 GDI 物件洩漏)
gSrc.ReleaseHdc(srcHdc);
gDest.ReleaseHdc(destHdc);
}
}
// 8. 儲存
string path = "ScreenCapture.jpg";
bmp.Save(path, ImageFormat.Jpeg);
Console.WriteLine($"截圖已儲存: {Path.GetFullPath(path)}");
}
}
}
finally
{
Gdi32.DeleteDC(screenDc);
}
}
}
public class CaptureForm : Form
{
private NotifyIcon trayIcon;
private ContextMenuStrip trayMenu;
public CaptureForm()
{
// 隱藏主視窗
this.WindowState = FormWindowState.Minimized;
this.ShowInTaskbar = false;
this.FormBorderStyle = FormBorderStyle.None;
this.Load += (s, e) => this.Hide();
// 系統匣圖示與選單
trayIcon = new NotifyIcon { Icon = SystemIcons.Application, Visible = true, Text = "螢幕擷取工具" };
trayMenu = new ContextMenuStrip();
trayMenu.Items.Add("擷取全螢幕", null, (s, e) => CaptureFullScreen());
trayMenu.Items.Add("擷取作用中視窗", null, (s, e) => CaptureActiveWindow()); // 需 GetForegroundWindow + GetWindowRect
trayMenu.Items.Add("-");
trayMenu.Items.Add("結束", null, (s, e) => Application.Exit());
trayIcon.ContextMenuStrip = trayMenu;
}
private void CaptureFullScreen()
{
// 呼叫上述核心流程,檔名含時間戳記
string file = $"Capture_{DateTime.Now:yyyyMMdd_HHmmss}.png";
// ... 截圖邏輯 ...
MessageBox.Show($"已儲存: {file}");
}
protected override void Dispose(bool disposing)
{
if (disposing) { trayIcon?.Dispose(); trayMenu?.Dispose(); }
base.Dispose(disposing);
}
}
| 模式 | 關鍵 API | 說明 |
|---|---|---|
| 特定視窗 | GetForegroundWindow(), GetWindowRect(), PrintWindow() | 僅擷取單一視窗客戶區 (含非客戶區需 WM_PRINT) |
| 指定區域 | 滑鼠選取矩形 → BitBlt 對應座標 | 區域截圖工具基礎 |
| 多螢幕 | Screen.AllScreens 迴圈建立各螢幕 Bitmap | 虛擬桌面座標系處理 |
| DirectX/遊戲畫面 | IDXGIOutputDuplication (Desktop Duplication API) | 繞過 GDI、高效能、需 Win8+ |
| 包含游標 | GetCursorInfo(), DrawIconEx() 繪製至 Bitmap | GDI BitBlt 不捕捉硬體游標 |
ScreenCapture.NET、SharpDX (Desktop Duplication 封裝)。GetHdc() 必須配對 ReleaseHdc(),否則 GDI 物件洩漏導致系統資源耗盡。Bitmap/Graphics 實作 IDisposable,務必 using 區塊包裹。CreateDC 回傳的 DC 需 DeleteDC 釋放。透過 P/Invoke 呼叫 GDI CreateDC/BitBlt 是傳統 .NET Framework 螢幕擷取的標準作法。理解 DC、Graphics、Bitmap 三者關係與資源釋放時機,能避免常見的 GDI 物件洩漏問題。現代 Windows 開發強烈建議遷移至 Windows.Graphics.Capture API 獲得更佳效能、安全性與功能支援。