我需要捕获没有背景图片(壁纸)的屏幕截图。我可以尝试禁用壁纸,截屏然后重新启用它,但是:
- 目前我不知道如何禁用/恢复壁纸(在简单的情况下,它是分配为带有一些平铺选项的桌面的图片文件,但它可以在现代版本的 Windows 中有所不同吗?)。
- 如果用户在我切换回壁纸之前杀死了应用程序,那么壁纸将保持禁用状态,这并不好。
有谁知道解决方案或想法在哪里寻找解决方案?也许可以暂时禁用壁纸?
更新:屏幕截图是注册错误程序的一部分,因此我需要所有可能有用的信息(可见表单、任务栏等),并且非常希望将屏幕截图保持为无损格式(更具可读性,更快的压缩)。其中一种选择是捕捉镜头以将它们存储为 AVI,因此处理时间也很重要。背景使图像更大,这是我试图删除它的唯一原因。我可以使用一些算法来减少使用的颜色,它极大地提高了压缩率,但这是一个耗时的过程。所以最好的办法是完全删除背景图片。
更新 2:为了从镜头序列中生成 AVI,我使用了 François PIETTE 的单位(基于这篇文章):
Avi := TAviFromBitmaps.CreateAviFile(
nil,
AviFilename,
MKFOURCC('S', 'C', 'L', 'S'), // msu-sc-codec
2, 1); // 2 frames per second
// called by timer
procedure TfrmSnapshot.RecordFrame;
begin
TakeSnapshot; // get snap shot to BMP:TBitmap
Avi.AppendNewFrame(Bmp.Handle);
end;
因此,如果我能够从快照中删除背景,AVI 压缩也会得到改善。
我使用的最终代码部分:
TAppRects = class
protected
FMonitor: TMonitor;
FRects: TList<TRect>;
function GetRegion(AArea: TRect): HRGN;
public
constructor Create(AMonitor: TMonitor);
destructor Destroy; override;
// fill all Area which is not covered by Rects (application forms)
procedure FillBackground(ABmp: TBitmap; AArea: TRect);
property Rects: TList<TRect> read FRects;
property Monitor: TMonitor read FMonitor;
end;
// Check for WS_EX_APPWINDOW will hide start button menu/popup menus outside of
// the forms etc, but it makes final AVI much smaller (and usually it is anough
// to have main forms recorded).
function EnumWindowsProc(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
var
r: TRect;
begin
Result := True;
if IsWindowVisible(hwnd) and
(GetWindow(hwnd, GW_OWNER)=0) and // is not owned by another window
(GetWindowLongPtr(hwnd, GWL_STYLE) and WS_EX_APPWINDOW<>0) and // is app
GetWindowRect(hwnd, r) and
(r.Width>0) and (r.Height>0)
then
with TAppRects(lParam) do
if (FMonitor=nil) or
(FMonitor.Handle=0) or
(FMonitor.Handle=MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST))
then
FRects.Add(r);
end;
{ TAppRects }
constructor TAppRects.Create(AMonitor: TMonitor);
begin
FMonitor := AMonitor;
FRects := TList<TRect>.Create;
EnumWindows(@EnumWindowsProc, NativeInt(self));
end;
destructor TAppRects.Destroy;
begin
FreeAndNil(FRects);
end;
function TAppRects.GetRegion(AArea: TRect): HRGN;
var
c: array of integer;
p: array of TPoint;
i: Integer;
begin
setlength(c, FRects.Count);
setlength(p, FRects.Count*4);
for i := 0 to FRects.Count-1 do
begin
c[i] := 4;
with FRects[i] do
begin
p[i*4 ] := Point(Left,Top);
p[i*4+1] := Point(Right,Top);
p[i*4+2] := Point(Right,Bottom);
p[i*4+3] := Point(Left,Bottom);
end;
end;
result := CreatePolyPolygonRgn(p[0], c[0], length(c), WINDING);
end;
procedure TAppRects.FillBackground(ABmp: TBitmap; AArea: TRect);
var
h1,h2,h3: HRGN;
begin
h1 := 0;
h2 := 0;
h3 := 0;
try
h1 := GetRegion(AArea);
if h1=0 then
exit;
h2 := CreateRectRgn(AArea.Left,AArea.Top,AArea.Right,AArea.Bottom);
h3 := CreateRectRgn(AArea.Left,AArea.Top,AArea.Right,AArea.Bottom);
if (h2<>0) and (h3<>0) and
not (CombineRgn(h3, h2,h1, RGN_DIFF) in [NULLREGION,RGN_ERROR])
then
FillRgn(ABmp.Canvas.Handle, h3, ABmp.Canvas.Brush.Handle);
finally
if h1<>0 then DeleteObject(h1);
if h2<>0 then DeleteObject(h2);
if h3<>0 then DeleteObject(h3);
end;
end;
procedure RemoveBackground(ASnapshot: TBitmap; AMonitor: TMonitor);
var
e: TAppRects;
c: TColor;
begin
e := nil;
try
e := TAppRects.Create(AMonitor);
c := ASnapshot.Canvas.Brush.Color;
ASnapshot.Canvas.Brush.Color := $FEA249; // kind of blue (~default for win8)
e.FillBackground(ASnapshot, e.Monitor.WorkareaRect);
ASnapshot.Canvas.Brush.Color := c;
finally
e.free;
end;
end;