4

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;
  • `GetFormImage()` draws the Form onto an in-memory `HDC` by simulating `WM_ERASEBKGND` and `WM_PAINT` messages to the Form, and then it copies that `HDC` to the final `TBitmap`. Thumbnails are not included in that painting. You cannot give an `HBITMAP` to `DwmRegisterThumbnail()`, and you cannot `BitBlt()` from an `HWND` directly. – Remy Lebeau Mar 02 '16 at 00:42
  • I would be very surprised if DWM is actually rendering the thumbnails directly onto the actual HWND, and not simply rendering it as part of the composited image of the HWND when it is displayed onscreen. DWM accomplishes its advanced visual effects and performance enhancements by manipulating bitmaps in memory, compositing multiple source bitmaps together, blending the pixels, etc, and then finally copying just the resulting image to the screen display. The thumbnail is likely just one of several bitmaps being rendered together to produce the HWND's visual image onscreen. – Remy Lebeau Mar 02 '16 at 00:47
  • @RemyLebeau, Then, in other words, not is possible make this that I want? –  Mar 02 '16 at 01:18
  • Not likely, no. Not with `DwmRegisterThumbnail()`. It is intended for displaying thumbnails onscreen to the user, not for capturing images. – Remy Lebeau Mar 02 '16 at 01:57
  • 1
    To capture an image of a hidden/minimized window, your best bet is likely going to be to simply show/restore the window momentarily, grab its image from the screen through traditional means (`GetDC(0)`, `BitBlt()`, etc), and then re-hide/minimize it when finshed. You can disable window animations to speed up the restore/minimize effect, and you can give the window an alpha opacity of 1 so it is technically *visible* to the OS but the user won't actually see it onscreen. Little tricks like that. – Remy Lebeau Mar 02 '16 at 01:57
  • 1
    @Ramy Lebeau, I tried using `TImage` for then save content to file, see above, I updated my question for show this. But comes a white screen capture. So, there some possibility of success with this my idea based on your tecnical knowledge? –  Mar 02 '16 at 03:53
  • 1
    Now you are passing the `TImage`'s internal `HDC` instead of an `HWND` to `DwmRegisterThumbnail()`. Do you understand the difference between an `HWND` (window), `HDC` (device context), `HBITMAP` (bitmap), etc? They represent different things, they are not interchangeable. `DwmRegisterThumbnail()` only works with `HWND`s, nothing else. You can get an `HDC` for an `HWND` using `GetDC()` or `GetWindowDC()`, for instance. But you cannot use an `HDC` where an `HWND` is expected, and vice versa. – Remy Lebeau Mar 02 '16 at 04:25
  • @RemyLebeau, already that nothing that I have tried had sucess, exists some other function of `DWM` api where I can get screenshot into a `TBitmap`? –  Mar 02 '16 at 11:16

1 Answers1

2

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

Zamrony P. Juhara
  • 5,222
  • 2
  • 24
  • 40
  • 1
    Using BitBlt() only works if the destination windows is fully opaque (alpha 255) and is visible to the user. What if I just want to get the raw image of an hidden window without displaying it ? Is it possible to create a fake HWND ? If not is it possible to intercept the paint event on the destination window just before it is painted ? Doesn it exist a Windows Message WM_ somenthing to achieve that ? Restore, capture and hide is not an option because it create "flickering" effect on the screen especially if you take more than one capture of the window. So what might be an alternative solution? – Bemipefe Apr 10 '19 at 11:10