Search code examples
delphibitmapscreenshotdwm

Past DWM screen capture to TBitmap


I have found a demo application that is able to get a screenshot of a minimized/hidden window using DwmRegisterThumbnail. It works perfect but the result image is draw in the form itself instead of a TBitmap.

This is the code:

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;

And the function is called in the following way:

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

Reference


The second parameter is the handle of the form itself. So far I tried to use GetFormImage but it does not capture the area where the captured window was drew. I have tried to get the image into a TBitmap in the following way but I have 2 problems:

          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. Get the correct size

2. Succeeded returns false when a handle of the TBitmap is used as the parameter.

It is possible to get the image into a TBitmap? Thanks in advance.

EDITED:

My last attempt using TImage, for then save Image content to file. But comes a white screen capture.

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;

Solution

  • DwmRegisterThumbnail is creating relationship between source window and destination windows so that when source window content is changed, its changed reflected in thumbnail preview.

    If you have window handle then you can capture its window visual representation into bitmap by using GetDC() and CreateCompatibleDC(), BitBlt(). See Capturing an Image