Предыдущий снимок экрана DWM в TBitmap

Я нашел демонстрационное приложение, которое может сделать снимок экрана свернутого / скрытого окна с помощью 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 следующим образом, но у меня есть 2 проблемы:

          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. Succeeded возвращает false, если дескриптор TBitmap используется в качестве параметра.

Можно ли поместить изображение в 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;

person Community    schedule 02.03.2016    source источник
comment
GetFormImage() рисует форму в HDC в памяти, моделируя сообщения WM_ERASEBKGND и WM_PAINT в форму, а затем копирует это HDC в окончательный TBitmap. Эскизы не включены в эту картину. Вы не можете поставить HBITMAP на DwmRegisterThumbnail(), и вы не можете BitBlt() напрямую от HWND.   -  person Remy Lebeau    schedule 02.03.2016
comment
Я был бы очень удивлен, если DWM фактически отображает эскизы непосредственно на фактическом HWND, а не просто отображает его как часть составного изображения HWND, когда оно отображается на экране. DWM выполняет свои расширенные визуальные эффекты и улучшения производительности, манипулируя растровыми изображениями в памяти, объединяя несколько исходных растровых изображений вместе, смешивая пиксели и т. Д., А затем, наконец, копируя только результирующее изображение на экран. Миниатюра, скорее всего, является лишь одним из нескольких растровых изображений, которые визуализируются вместе для создания визуального изображения HWND на экране.   -  person Remy Lebeau    schedule 02.03.2016
comment
@RemyLebeau, то, другими словами, не возможно ли сделать то, что я хочу?   -  person    schedule 02.03.2016
comment
Вряд ли, нет. Только не с DwmRegisterThumbnail(). Он предназначен для отображения миниатюр на экране пользователю, а не для захвата изображений.   -  person Remy Lebeau    schedule 02.03.2016
comment
Чтобы захватить изображение скрытого / свернутого окна, лучше всего, вероятно, будет просто показать / восстановить окно на мгновение, захватить его изображение с экрана традиционными средствами (GetDC(0), BitBlt() и т. Д.), А затем снова скрыть / минимизировать его при окончании. Вы можете отключить анимацию окна, чтобы ускорить эффект восстановления / минимизации, и вы можете дать окну альфа-прозрачность, равную 1, чтобы оно было технически видимым для ОС, но пользователь на самом деле не увидит его на экране . Маленькие уловки вроде этого.   -  person Remy Lebeau    schedule 02.03.2016
comment
@Ramy Lebeau, я пробовал использовать TImage для сохранения содержимого в файл, см. Выше, я обновил свой вопрос, чтобы показать это. Но идет белый снимок экрана. Итак, есть ли вероятность успеха в этой моей идее, основанной на ваших технических знаниях?   -  person    schedule 02.03.2016
comment
Теперь вы передаете TImage внутренний HDC вместо HWND в DwmRegisterThumbnail(). Вы понимаете разницу между HWND (окно), HDC (контекст устройства), HBITMAP (растровое изображение) и т. Д.? Они представляют собой разные вещи, они не взаимозаменяемы. DwmRegisterThumbnail() работает только с HWNDs, больше ничего. Вы можете получить HDC за HWND, используя, например, GetDC() или GetWindowDC(). Но вы не можете использовать HDC там, где ожидается HWND, и наоборот.   -  person Remy Lebeau    schedule 02.03.2016
comment
@RemyLebeau, уже ничего из того, что я пробовал, не имело успеха, существует другая функция DWM api, где я могу получить снимок экрана в TBitmap?   -  person    schedule 02.03.2016


Ответы (1)


DwmRegisterThumbnail создается взаимосвязь между исходным окном и целевым окнами, так что при изменении содержимого исходного окна его изменение отражается в предварительном просмотре эскизов.

Если у вас есть дескриптор окна, вы можете записать его визуальное представление окна в растровое изображение, используя GetDC() и CreateCompatibleDC(), BitBlt(). См. Захват изображения

person Zamrony P. Juhara    schedule 02.03.2016
comment
Использование BitBlt () работает только в том случае, если целевые окна полностью непрозрачны (альфа 255) и видны пользователю. Что, если я просто хочу получить необработанное изображение скрытого окна, не отображая его? Можно ли создать поддельный HWND? Если нет, можно ли перехватить событие рисования в окне назначения непосредственно перед его рисованием? Разве для этого не существует Windows Message WM_ somenthing? Восстановление, захват и скрытие - это не вариант, потому что он создает эффект мерцания на экране, особенно если вы делаете более одного снимка окна. Так что может быть альтернативным решением? - person Bemipefe; 10.04.2019