WPF を使用してスクリーン キャプチャ アプリケーションを使用しています。アプリケーションウィンドウをキャプチャするだけです。ウィンドウだけでなく背景もキャプチャするコードがあります。背景をキャプチャするのではなく、ウィンドウだけをキャプチャしたくありません。サイズが変更された場合でも、ウィンドウのみが実行時にキャプチャされる必要があります。私はWPFに少し慣れていません。これで私を助けてください。これが私のコードです:
private void ShowExistingWindow(string processName)
{
var processes = Process.GetProcessesByName(processName);
foreach (var process in processes)
{
// the single-instance already open should have a MainWindowHandle
if (process.MainWindowHandle != IntPtr.Zero)
{
// restores the window in case it was minimized
ShowWindow(process.MainWindowHandle, SW_SHOWNORMAL);
// brings the window to the foreground
SetForegroundWindow(process.MainWindowHandle);
}
}
}
このコードの問題は、ウィンドウが復元され、背景と共にキャプチャされることです。
ウィンドウのキャプチャに使用しているコードは次のとおりです。
public static BitmapSource CaptureWindow(IntPtr hWnd, bool recolorBackground, Color SubstituteBackgroundColor, bool addToClipboard)
{
Int32Rect rect = GetWindowActualRect(hWnd);
Window blankingWindow = null;
if (recolorBackground)
{
blankingWindow = new Window();
blankingWindow.WindowStyle = WindowStyle.None;
blankingWindow.Title = string.Empty;
blankingWindow.ShowInTaskbar = false;
blankingWindow.AllowsTransparency = true;
blankingWindow.Background = new SolidColorBrush(substituteBackgroundColor);
blankingWindow.Show();
int fudge = 20;
blankingWindow.Left = rect.X - fudge / 2;
blankingWindow.Top = rect.Y - fudge / 2;
blankingWindow.Width = rect.Width + fudge;
blankingWindow.Height = rect.Height + fudge;
}
// bring the to-be-captured window to capture to the foreground
// there's a race condition here where the blanking window
// sometimes comes to the top. Hate those. There is surely
// a non-WPF native solution to the blanking window which likely
// involves drawing directly on the desktop or the target window
SetForegroundWindow(hWnd);
BitmapSource captured = CaptureRegion(
hWnd,
rect.X,
rect.Y,
rect.Width,
rect.Height,
true);
if (blankingWindow != null)
blankingWindow.Close();
return captured;
}
// this accounts for the border and shadow. Serious fudgery here.
private static Int32Rect GetWindowActualRect(IntPtr hWnd)
{
Win32Rect windowRect = new Win32Rect();
Win32Rect clientRect = new Win32Rect();
User32.GetWindowRect(hWnd, out windowRect);
User32.GetClientRect(hWnd, out clientRect);
int sideBorder = (windowRect.Width - clientRect.Width)/2 + 1;
// sooo, yeah.
const int hackToAccountForShadow = 4;
Win32Point topLeftPoint = new Win32Point(windowRect.Left - sideBorder, windowRect.Top - sideBorder);
User32.ClientToScreen(hWnd, ref topLeftPoint);
Int32Rect actualRect = new Int32Rect(
topLeftPoint.X,
topLeftPoint.Y,
windowRect.Width + sideBorder * 2 + hackToAccountForShadow,
windowRect.Height + sideBorder * 2 + hackToAccountForShadow);
return actualRect;
}
public static BitmapSource CaptureRegion(IntPtr hWnd, int x, int y, int width, int height, bool addToClipboard) {
IntPtr sourceDC = IntPtr.Zero;
IntPtr targetDC = IntPtr.Zero;
IntPtr compatibleBitmapHandle = IntPtr.Zero;
BitmapSource bitmap = null;
try
{
width = User32.GetSystemMetrics(User32.SCREEN_X);
height = User32.GetSystemMetrics(User32.SCREEN_Y);
// gets the main desktop and all open windows
sourceDC = GetDC(GetDesktopWindow());
//sourceDC = User32.GetDC(hWnd);
targetDC = CreateCompatibleDC(sourceDC);
// create a bitmap compatible with our target DC
compatibleBitmapHandle = CreateCompatibleBitmap(sourceDC, width, height);
// gets the bitmap into the target device context
SelectObject(targetDC, compatibleBitmapHandle);
// copy from source to destination
BitBlt(targetDC, 0, 0, width, height, sourceDC, x, y, SRCCOPY);
// Here's the WPF glue to make it all work. It converts from an
// hBitmap to a BitmapSource.
bitmap = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
compatibleBitmapHandle, IntPtr.Zero, Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
catch (Exception ex)
{
throw new Exception(string.Format("Error capturing region {0},{1},{2},{3}", x, y, width, height), ex);
}
finally
{
DeleteObject(compatibleBitmapHandle);
ReleaseDC(IntPtr.Zero, sourceDC);
ReleaseDC(IntPtr.Zero, targetDC);
}
return bitmap;
}