本章介紹如何通過句柄,截取指定窗口內容,以及截取失敗的場景
一、根據窗口句柄獲取窗口截圖
先創建一個測試窗口程序A,顯示如下:
同時我們把此窗口的句柄顯示到一個文本輸入框內。
1 TestBox.Text = new WindowInteropHelper(this).Handle.ToString();
如上圖所示,1774674是此窗口的句柄值。
然后,我們新建一個窗口程序B,對窗口A進行截圖並展示
1 var windowIntPtr = new IntPtr(1774674); 2 var bitmapImage = GetWindowShotCut(windowIntPtr); 3 TestImage.Source = bitmapImage;
截圖方法及詳細操作如下:
1 private BitmapImage GetWindowShotCut(IntPtr intPtr) 2 { 3 var image = WindowCaptureHelper.GetShotCutImage(intPtr); 4 var bitmapImage = BitmapConveters.ConvertToBitmapImage(image); 5 return bitmapImage; 6 }
WindowCaptureHelper:

1 public class WindowCaptureHelper 2 { 3 [DllImport("user32.dll")] 4 private static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rectangle rect); 5 [DllImport("gdi32.dll")] 6 private static extern IntPtr CreateCompatibleDC(IntPtr hdc); 7 [DllImport("gdi32.dll")] 8 private static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight); 9 [DllImport("gdi32.dll")] 10 private static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj); 11 [DllImport("gdi32.dll")] 12 private static extern int DeleteDC(IntPtr hdc); 13 [DllImport("user32.dll")] 14 private static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, int nFlags); 15 [DllImport("user32.dll")] 16 private static extern IntPtr GetWindowDC(IntPtr hwnd); 17 18 public static Bitmap GetShotCutImage(IntPtr hWnd) 19 { 20 var hscrdc = GetWindowDC(hWnd); 21 var windowRect = new Rectangle(); 22 GetWindowRect(hWnd, ref windowRect); 23 int width = Math.Abs(windowRect.Width- windowRect.X); 24 int height = Math.Abs(windowRect.Height- windowRect.Y); 25 var hbitmap = CreateCompatibleBitmap(hscrdc, width, height); 26 var hmemdc = CreateCompatibleDC(hscrdc); 27 SelectObject(hmemdc, hbitmap); 28 PrintWindow(hWnd, hmemdc, 0); 29 var bmp = Image.FromHbitmap(hbitmap); 30 DeleteDC(hscrdc); 31 DeleteDC(hmemdc); 32 return bmp; 33 } 34 }
BitmapConveters:

1 public class BitmapConveters 2 { 3 [DllImport("gdi32")] 4 static extern int DeleteObject(IntPtr o); 5 public static BitmapSource ConvertToBitMapSource(Bitmap bitmap) 6 { 7 IntPtr intPtrl = bitmap.GetHbitmap(); 8 BitmapSource bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(intPtrl, 9 IntPtr.Zero, 10 Int32Rect.Empty, 11 BitmapSizeOptions.FromEmptyOptions()); 12 DeleteObject(intPtrl); 13 return bitmapSource; 14 } 15 public static BitmapImage ConvertToBitmapImage(Bitmap bitmap) 16 { 17 using (MemoryStream stream = new MemoryStream()) 18 { 19 bitmap.Save(stream, ImageFormat.Bmp); 20 stream.Position = 0; 21 BitmapImage result = new BitmapImage(); 22 result.BeginInit(); 23 result.CacheOption = BitmapCacheOption.OnLoad; 24 result.StreamSource = stream; 25 result.EndInit(); 26 result.Freeze(); 27 return result; 28 } 29 } 30 }
截圖后顯示如下:
二、窗口截圖失敗
窗口A在特定場景下,我們截到的窗口內容是黑色的:
截圖獲取失敗了,窗口A做了什么處理?
定位發現是屬性AllowsTransparency="True"的鍋,搜索了下,網上也有同樣的反饋:
- Taking a screenshot of windows with AllowsTransparency="True" (microsoft.com)
- c# - Capture transparent WPF Window for streaming - Stack Overflow
- .net - Transparent child window renders as black when screen sharing main window on Microsoft Teams - Stack Overflow
官方大佬說,這是他們的一個BUG。在win10 2004更新版本中,已處理。
不過,我現在是win11,依然還有問題。。。我是在win10上直接更新到win11,可能原來那個win10企業LTSC版本有點老,win11更新只更新了UI?或者win11是基於原來舊分支開發的?等回復中...Taking a screenshot of windows with AllowsTransparency="True" · Issue #358 · microsoft/WindowsCompositionSamples (github.com)