将过去的DWM屏幕截图转换为TBitmap

4
我找到了一个演示应用程序,可以使用DwmRegisterThumbnail获取最小化/隐藏窗口的屏幕截图。它运行完美,但结果图像是在表单本身中绘制而不是TBitmap中。
这是代码:
procedure TfrmMain.PreviewWindow(const ASource, ADest: HWND; const ARect: TRect);
var
  LResult: HRESULT;
  LThumpProp: DWM_THUMBNAIL_PROPERTIES;
begin
  if NOT DwmCompositionEnabled then begin
    MessageDlg('DWM composition is NOT enabled.', mtWarning, [mbOK], 0);
    Exit;
  end;

  PreviewDisable;
  FPreviewEnabled := Succeeded(DwmRegisterThumbnail(ADest, ASource, @FTumbnail));
  if FPreviewEnabled then begin

    LThumpProp.dwFlags := DWM_TNP_SOURCECLIENTAREAONLY or DWM_TNP_VISIBLE or
      DWM_TNP_OPACITY or DWM_TNP_RECTDESTINATION;
    LThumpProp.fSourceClientAreaOnly := False;
    LThumpProp.fVisible := True;
    LThumpProp.opacity := 200;
    LThumpProp.rcDestination := ARect;
    LResult := DwmUpdateThumbnailProperties(FTumbnail, LThumpProp);
    FPreviewEnabled := (LResult = S_OK);
  end else
    MessageDlg('Cannot link to window  ' + IntToStr(ASource), mtError, [mbOK], 0);
end;

以下是调用该函数的方式:

PreviewWindow( TargetWindow.Handle,  Self.Handle,  LRect);

参考


第二个参数是表单本身的句柄。到目前为止,我尝试使用GetFormImage,但它没有捕获绘制捕获窗口的区域。我尝试以以下方式将图像获取到TBitmap中,但我有两个问题:

          procedure TfrmMain.PreviewWindow(const ASource, ADest: HWND; const ARect: TRect);
            var
              LResult: HRESULT;
              LThumpProp: DWM_THUMBNAIL_PROPERTIES;
              Bitmap: TBitmap;
              Width, Height: integer;
            begin
              if NOT DwmCompositionEnabled then begin
                MessageDlg('DWM composition is NOT enabled.', mtWarning, [mbOK], 0);
                Exit;
              end; // if NOT DwmCompositionEnabled then begin
              Bitmap := TBitmap.Create;

              try
              Width:=500; //default size....
              Height:=500;
                Bitmap.SetSize(Width, Height);

              PreviewDisable;
              //THE FOLLOWING LINE RETURN FALSE WHEN BITMAP.HANDLE IS USED INSTEAD OF ADest
              FPreviewEnabled := Succeeded(DwmRegisterThumbnail(Bitmap.Handle, ASource, @FTumbnail));
              if FPreviewEnabled then begin

                LThumpProp.dwFlags := DWM_TNP_SOURCECLIENTAREAONLY or DWM_TNP_VISIBLE or
                  DWM_TNP_OPACITY or DWM_TNP_RECTDESTINATION;
                LThumpProp.fSourceClientAreaOnly := False;
                LThumpProp.fVisible := True;
                LThumpProp.opacity := 200;
                LThumpProp.rcDestination := ARect;
                LResult := DwmUpdateThumbnailProperties(FTumbnail, LThumpProp);
                FPreviewEnabled := (LResult = S_OK);
                BitBlt(Bitmap.Canvas.Handle, 0, 0, Width, Height, ADest, 0, 0, SRCCOPY);
                Bitmap.SaveToFile('d:\test.bmp'); //Test if the image is correct
              end else
                MessageDlg('Cannot link to window  ' + IntToStr(ASource), mtError, [mbOK], 0);
              finally
                Bitmap.Free;
              end;
            end;

1. 获取正确的尺寸

2. 当使用TBitmap的句柄作为参数时,Succeeded返回false。

是否可以将图像放入TBitmap中?提前感谢。

编辑:

我最近尝试使用TImage,然后将图像内容保存到文件中。但是出现了白屏截图。

uses
 dwmapi;

private
    { Private declarations }
    thumb: PHTHUMBNAIL;

    function UpdateThumb(aThumb: PHTHUMBNAIL; aDestRect: TRect):boolean;
    var
     size: PSize;
     props: PDWM_THUMBNAIL_PROPERTIES;
    begin
        result:=true;
        if aThumb <> nil then
        begin
          DwmQueryThumbnailSourceSize(aThumb^, size);
          props.dwFlags:=DWM_TNP_VISIBLE and DWM_TNP_RECTDESTINATION and DWM_TNP_OPACITY;
          props.fVisible:=true;
          props.opacity:=50;
          props.fSourceClientAreaOnly:=false;
          props.rcDestination:= aDestRect;

          if (size.cx < aDestRect.Right - aDestRect.Left) then props.rcDestination.Right:=props.rcDestination.Left+size.cx;
          if (size.cy < aDestRect.Bottom - aDestRect.Top) then props.rcDestination.Top:=props.rcDestination.Left+size.cy;

          DwmUpdateThumbnailProperties(aThumb^,props^);
        end;

    end;


    procedure TForm1.btn1Click(Sender: TObject);
    var
     h: Hwnd;
     r: TRect;
     wwidth, wheight: integer;
     i: integer;
    begin
     h:=FindWindow(nil,'Untitled - Notepad');

       if h<>0 then
       begin
         GetWindowRect(h,r);
         wwidth:=r.Right-r.Left;
         wheight:=r.Bottom-r.Top;

         if thumb <> nil then
         begin
           DwmUnregisterThumbnail(thumb^);
           thumb := nil;
         end;

         i:=DwmRegisterThumbnail(img1.canvas.Handle,h,thumb);
         if i=0 then
         UpdateThumb(thumb, Rect(0,0,Img1.Width, Img1.Height));
       end;

GetFormImage()通过模拟 WM_ERASEBKGNDWM_PAINT 消息到窗体上,将窗体绘制到内存 HDC 上,并将该 HDC 复制到最终的 TBitmap 中。 缩略图不包括在此绘制中。 您无法将 HBITMAP 提供给DwmRegisterThumbnail(),也不能直接从 HWND 进行 BitBlt() - Remy Lebeau
@RemyLebeau,换句话说,我想要的这个是不可能实现的吗? - user4805785
1
要捕获隐藏/最小化窗口的图像,最好的方法可能是简单地短暂显示/恢复窗口,通过传统手段(GetDC(0)BitBlt()等)从屏幕上获取其图像,然后在完成后重新隐藏/最小化它。您可以禁用窗口动画以加快恢复/最小化效果,并且可以将窗口的alpha不透明度设置为1,这样在技术上它对OS是可见的,但用户实际上看不到它在屏幕上。有一些小技巧像这样。 - Remy Lebeau
1
@Ramy Lebeau,我尝试使用TImage然后保存内容到文件,见上面,我更新了我的问题以展示这个。但是截图是白色的。所以,基于您的技术知识,这个想法有可能成功吗? - user4805785
1
现在您正在传递TImage的内部HDC而不是HWNDDwmRegisterThumbnail()。您是否了解HWND(窗口)、HDC(设备上下文)、HBITMAP(位图)等之间的区别?它们代表不同的东西,它们不能互换。DwmRegisterThumbnail()仅适用于HWND,不适用于其他任何内容。例如,您可以使用GetDC()GetWindowDC()获取HWNDHDC。但是您不能在需要HWND的地方使用HDC,反之亦然。 - Remy Lebeau
显示剩余3条评论
1个回答

2

DwmRegisterThumbnail创建源窗口和目标窗口之间的关系,以便当源窗口内容发生更改时,其更改会反映在缩略图预览中。

如果您有窗口句柄,则可以使用GetDC()CreateCompatibleDC()BitBlt()将其窗口可视表示捕获到位图中。请参见捕获图像


1
仅使用BitBlt()只有在目标窗口完全不透明(alpha 255)且对用户可见时才起作用。如果我只想获取隐藏窗口的原始图像而不显示它怎么办?是否有可能创建一个虚假的HWND?如果不是,是否有可能在目标窗口绘制之前拦截绘制事件?是否存在Windows消息WM_某些内容来实现这一点?恢复、捕获和隐藏不是一个选择,因为它会在屏幕上产生“闪烁”效果,特别是如果你多次捕获窗口。那么可能有什么替代方案呢? - Bemipefe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接