Problems getting a window capture with Bitblt and PrintWindow.
Hello, I'm trying to get a window capture through the bitblt capture method and printWindow api, but in both I have the problem with some processes (Mostly games) where it only captures the window showing the white or black content (depending on the computer).
Here I can show how they are observed in each one.
[Using Bitblt]
[Using PrintWindow]
I have noticed that OBS has Bitblt as a capture method, where it also presents the same problem.
But if "Windows 10" capture mode is used, it is solved. Is this mode available to use in vb6?
Thank you very much, this has me worried and it would be good to have information!.
Re: Problems getting a window capture with Bitblt and PrintWindow.
@Franky: A clever piece of code although painful to watch Invoke-ing all those calls without a typelib. I'll hoist RoGetActivationFactory and RoActivateInstance API declares immediately to test WinRT JSON classes :-))
Also note that the link to your ZIP upload will be removed by these forums in a short time. All your previous back-links are removed too which is a shame as the code is solid.
Can you please link *and* paste relevant code in the thread so it becomes searchable in google and with forum's advanced search?
Re: Problems getting a window capture with Bitblt and PrintWindow.
This is a difficult problem. Games and videos are difficult to capture images.
But in games or videos, DX or Opengl is used to draw pictures, which naturally cannot be cut off
Re: Problems getting a window capture with Bitblt and PrintWindow.
For some games you can BitBlt from their window hDC and for others you can't, even though they obviously all use DirectX. I don't know what makes the difference. On the other hand you can always BitBlt from the desktop hDC so just calculate the coordinates of the game window.
Last edited by VanGoghGaming; Oct 13th, 2023 at 04:48 PM.
Re: Problems getting a window capture with Bitblt and PrintWindow.
To answer the initial post, it seems that "PrintWindow" has been modified (since Windows 8.1) to work with DirectX surfaces. Now it has an undocumented parameter (PW_RENDERFULLCONTENT):
Code:
Public Const PW_CLIENTONLY = 1, PW_RENDERFULLCONTENT = 2
PrintWindow SourceWindow_hWnd, Destination_hDC, PW_CLIENTONLY Or PW_RENDERFULLCONTENT
Just tested this and it seems to be working correctly.
Last edited by VanGoghGaming; Oct 13th, 2023 at 04:48 PM.
Re: Problems getting a window capture with Bitblt and PrintWindow.
Thank you very much!! -Franky-, VanGoghGaming. The solutions are excellent for this problem.!
VBC_UwpCapturePicker.zip and PrintWindow has surprised me, They got me out of big trouble!
Re: Problems getting a window capture with Bitblt and PrintWindow.
Originally Posted by wqweto
A clever piece of code although painful to watch Invoke-ing all those calls without a typelib.
Is it even possible to include all those objects and their methods in a typelib? So far all the VB6 typelibs I've seen include only DLL function declarations and basic types (long, integer, byte)...
I tried to take a look at Franky's code and got dizzy from all the invoking of "DispCallFunc"!
Re: Problems getting a window capture with Bitblt and PrintWindow.
Originally Posted by -Franky-
If I read that correctly in the MS Docs, hString must be deleted via WindowsDeleteString if it was created with WindowsCreateString. Otherwise not.
Pretty sure that for every [out] HSTRING *value parameter you have to call WindowsDeleteString when done working with the HSTRING. This is what WinRT/C++ wrappers do when returning HSTRING with return { value, take_ownership_from_abi } which ensures Close method is called on the handle (the method which wraps native WindowsDeleteString).
You already do this in GetInspectable_GetRuntimeClassName which is correct but in most other cases where VarPtr(hString) is populated as an output parameter the HSTRING is leaking (e.g. ToString, GetStringAt, Stringify, GetString in clsJsonArray)
Another possible source of leaks is when missing calling (manually) Release on the raw pointers in Long variables. These could be declared as IUnknown and let VB6 automagically call Release at correct places with no leaks possible.
Otherwise the proof of concept is great, this is something I wanted to research for a long time and will probably invest some time wrapping base WinRT interfaces in a typelib for performance reasons (Invoke-ing is expensive).
cheers,
</wqw>
Last edited by wqweto; Jan 10th, 2023 at 03:19 AM.
Re: Problems getting a window capture with Bitblt and PrintWindow.
Originally Posted by wqweto
Pretty sure that for every [out] HSTRING *value parameter you have to call WindowsDeleteString when done working with the HSTRING. This is what WinRT/C++ wrappers do when returning HSTRING with return { value, take_ownership_from_abi } which ensures Close method is called on the handle (the method which wraps native WindowsDeleteString).
You already do this in GetInspectable_GetRuntimeClassName which is correct but in most other cases where VarPtr(hString) is populated as an output parameter the HSTRING is leaking (e.g. ToString, GetStringAt, Stringify, GetString in clsJsonArray)
Another possible source of leaks is when missing calling (manually) Release on the raw pointers in Long variables. These could be declared as IUnknown and let VB6 automagically call Release at correct places with no leaks possible.
Otherwise the proof of concept is great, this is something I wanted to research for a long time and will probably invest some time wrapping base WinRT interfaces in a typelib for performance reasons (Invoke-ing is expensive).
cheers,
</wqw>
OK. then I misunderstood the MS Docs. Thank you for your explanation.
Private Const WindowsGraphicsCaptureGraphicsCaptureItem As String = "Windows.Graphics.Capture.GraphicsCaptureItem"
Private Const IID_IGraphicsCaptureItemInterop As String = "{3628e81b-3cac-4c60-b7f4-23ce0e0c3356}"
Private Const IID_IGraphicsCaptureItem As String = "{79c3f95b-31f7-4ec2-a464-632ef5d30760}"
Private Enum vtb_Interfaces
' ...
' IGraphicsCaptureItemInterop
IGraphicsCaptureItemInterop_CreateForWindow = 3
IGraphicsCaptureItemInterop_CreateForMonitor = 4
End Enum
Private m_pIGraphicsCaptureItemInterop As Long
Private Sub Class_Initialize()
If GetActivationFactory(WindowsGraphicsCaptureGraphicsCaptureItem, _
IID_IGraphicsCaptureItemInterop, _
m_pIGraphicsCaptureItemInterop) Then
' ....
End Sub
Public Function CaptureHwnd(ByVal hwnd As Long) As Boolean
If m_pIGraphicsCaptureItemInterop <> 0& And hwnd <> 0& Then
Dim pIGraphicsCaptureItem As Long
If Invoke(m_pIGraphicsCaptureItemInterop, _
IGraphicsCaptureItemInterop_CreateForWindow, _
hwnd, _
VarPtr(Str2Guid(IID_IGraphicsCaptureItem)), _
VarPtr(pIGraphicsCaptureItem)) = S_OK Then
' etc.
' CaptureHwnd = True
Call Release(pIGraphicsCaptureItem)
End If
End If
End Function
Last edited by -Franky-; Jan 11th, 2023 at 02:53 AM.
Re: Problems getting a window capture with Bitblt and PrintWindow.
Hey Franky, thanks for the code sample! I think I got the hang of it now, it's not really difficult once you step through the code in the debugger. I really wish you could do something like CreateObject("Windows.Graphics.Capture") though... Invoking all those methods without instantiating their classes is terribly slow:
Taking a screenshot with this code sample: 92ms
Taking the same screenshot with "PrintWindow" with the "PW_RENDERFULLCONTENT" flag (which probably does the same thing under the hood): 19ms
Taking the same screenshot with "BitBlt" (for comparison purposes): 0.7 ms
All measurements done inside the IDE.
Incidentally I have stumbled upon this piece of code which does exactly the same thing as yours, only using the AutoHotkey scripting language:
Re: Problems getting a window capture with Bitblt and PrintWindow.
Originally Posted by VanGoghGaming
Hey Franky, thanks for the code sample! I think I got the hang of it now, it's not really difficult once you step through the code in the debugger. I really wish you could do something like CreateObject("Windows.Graphics.Capture") though... Invoking all those methods without instantiating their classes is terribly slow:
Taking a screenshot with this code sample: 92ms
Taking the same screenshot with "PrintWindow" with the "PW_RENDERFULLCONTENT" flag (which probably does the same thing under the hood): 19ms
Taking the same screenshot with "BitBlt" (for comparison purposes): 0.7 ms
All measurements done inside the IDE.
Incidentally I have stumbled upon this piece of code which does exactly the same thing as yours, only using the AutoHotkey scripting language:
The similarities are uncanny, almost as if it's the same code translated in VB6!
Caught. Let's put it that way. I copied a few things from this AutoHotKey Script and some comes directly from Microsoft examples.
I think the code is correspondingly slow because of the invoking. BitBlt has the disadvantage that the window has to be in the foreground. Not via the WinRT.
Re: Problems getting a window capture with Bitblt and PrintWindow.
Hey no worries, you did a fine job nevertheless. I did some reading on this WinRT stuff, apparently it's not so easy to instantiate WinRT objects in VB6 (if even possible at all) compared to the rest of COM objects.
BitBlt does work with background windows even if they are completely covered by other windows as long as they are not minimized. It just fails on some windows that use "special" DirectX techniques to draw their content (because it can also work fine with other DirectX windows)...
Re: Problems getting a window capture with Bitblt and PrintWindow.
Originally Posted by xxdoc123
BitBlt cannot work with background windows!
While that may have been true in previous versions of Windows, somewhere along the way Microsoft woke up and got their act together. Anyway it's pretty easy to test it yourself, just make a new project with two forms and a timer on each. "Form2" has a "Label" starting with "1" and the timer keeps incrementing it each second. In "Form1" the timer performs "BitBlt" from "Form2.hDC" each second, like this:
Form1:
Code:
Option Explicit
Private Sub Form_Load()
Form2.Show
End Sub
Private Sub Form_Unload(Cancel As Integer)
Unload Form2
End Sub
Private Sub Timer1_Timer()
BitBlt Me.hDC, 0, 0, Form2.ScaleWidth, Form2.ScaleHeight, Form2.hDC, 0, 0, vbSrcCopy
End Sub
Form2:
Code:
Option Explicit
Private Sub Timer1_Timer()
Label1 = Val(Label1) + 1
End Sub
Now try to hide "Form2" somewhere in the dark recesses of your desktop, behind a browser window for example. "BitBlt" will continue to work just fine on "Form1", hell it even works if both windows are in the background.
Last edited by VanGoghGaming; Oct 13th, 2023 at 04:48 PM.
Re: Problems getting a window capture with Bitblt and PrintWindow.
Originally Posted by VanGoghGaming
While that may have been true in previous versions of Windows, somewhere along the way Microsoft woke up and got their act together. Anyway it's pretty easy to test it yourself, just make a new project with two forms and a timer on each. "Form2" has a "Label" starting with "1" and the timer keeps incrementing it each second. In "Form1" the timer performs "BitBlt" from "Form2.hDC" each second, like this:
Form1:
Code:
Option Explicit
Private Sub Form_Load()
Form2.Show
End Sub
Private Sub Form_Unload(Cancel As Integer)
Unload Form2
End Sub
Private Sub Timer1_Timer()
BitBlt Me.hDC, 0, 0, Form2.ScaleWidth, Form2.ScaleHeight, Form2.hDC, 0, 0, vbSrcCopy
End Sub
Form2:
Code:
Option Explicit
Private Sub Timer1_Timer()
Label1 = Val(Label1) + 1
End Sub
Now try to hide "Form2" somewhere in the dark recesses of your desktop, behind a browser window for example. "BitBlt" will continue to work just fine on "Form1", hell it even works if both windows are in the background.
from1 and from2 in the same exe .
if you test form2 in other exe ,you will see different
Re: Problems getting a window capture with Bitblt and PrintWindow.
Originally Posted by xxdoc123
if you test form2 in other exe ,you will see different
Generally it doesn't bode well to make such bold, blanket statements without at least bothering to write 4 simple lines of code... In this example the actual game is running in the background behind the VB6 IDE window:
And the full screenshot (which for some reason is too small to read the text when uploaded):
Last edited by VanGoghGaming; Oct 13th, 2023 at 04:49 PM.
Re: Problems getting a window capture with Bitblt and PrintWindow.
thanks ,In my memory, I can't take screenshots in the background used bitblt if some form out of the range of the display . So I used printwindow instead~
some hacker can hook dx and screenshot.but can not find any code ~
Private Const WindowsGraphicsCaptureGraphicsCaptureItem As String = "Windows.Graphics.Capture.GraphicsCaptureItem"
Private Const IID_IGraphicsCaptureItemInterop As String = "{3628e81b-3cac-4c60-b7f4-23ce0e0c3356}"
Private Const IID_IGraphicsCaptureItem As String = "{79c3f95b-31f7-4ec2-a464-632ef5d30760}"
Private Enum vtb_Interfaces
' ...
' IGraphicsCaptureItemInterop
IGraphicsCaptureItemInterop_CreateForWindow = 3
IGraphicsCaptureItemInterop_CreateForMonitor = 4
End Enum
Private m_pIGraphicsCaptureItemInterop As Long
Private Sub Class_Initialize()
If GetActivationFactory(WindowsGraphicsCaptureGraphicsCaptureItem, _
IID_IGraphicsCaptureItemInterop, _
m_pIGraphicsCaptureItemInterop) Then
' ....
End Sub
Public Function CaptureHwnd(ByVal hwnd As Long) As Boolean
If m_pIGraphicsCaptureItemInterop <> 0& And hwnd <> 0& Then
Dim pIGraphicsCaptureItem As Long
If Invoke(m_pIGraphicsCaptureItemInterop, _
IGraphicsCaptureItemInterop_CreateForWindow, _
hwnd, _
VarPtr(Str2Guid(IID_IGraphicsCaptureItem)), _
VarPtr(pIGraphicsCaptureItem)) = S_OK Then
' etc.
' CaptureHwnd = True
Call Release(pIGraphicsCaptureItem)
End If
End If
End Function
thank you but how can add it to the cls?
If GetActivationFactory(WindowsGraphicsCaptureGraphicsCaptureItem, IID_IGraphicsCaptureItemInterop, m_pIGraphicsCaptureItemInterop) Then
Dim bolIsSupported As Boolean
If Invoke(pIGraphicsCaptureSessionStatics, IGraphicsCaptureSessionStatics_IsSupported, VarPtr(bolIsSupported)) = S_OK Then may be not return S_OK
Re: Problems getting a window capture with Bitblt and PrintWindow.
Originally Posted by xxdoc123
thank you but how can add it to the cls?
If GetActivationFactory(WindowsGraphicsCaptureGraphicsCaptureItem, IID_IGraphicsCaptureItemInterop, m_pIGraphicsCaptureItemInterop) Then
Dim bolIsSupported As Boolean
If Invoke(pIGraphicsCaptureSessionStatics, IGraphicsCaptureSessionStatics_IsSupported, VarPtr(bolIsSupported)) = S_OK Then may be not return S_OK
thanks .
First: The WinRT code works on Windows 10 and higher. What does the Debug.Print "0x" & Hex$(varRet) return in the OleInvoke function when you call IGraphicsCaptureSessionStatics.IsSupported?
The call GetActivationFactory(WindowsGraphicsCaptureGraphicsCaptureItem.... goes after IGraphicsCaptureSessionStatics.IsSupported. If IGraphicsCaptureSessionStatics.IsSupported already fails, then the rest won't work either.
Re: Problems getting a window capture with Bitblt and PrintWindow.
thanks。i have modiy the cls and work fine
Code:
......
Private Const WindowsGraphicsCaptureGraphicsCaptureItem As String = "Windows.Graphics.Capture.GraphicsCaptureItem"
Private Const IID_IGraphicsCaptureItemInterop As String = "{3628e81b-3cac-4c60-b7f4-23ce0e0c3356}"
Private Const IID_IGraphicsCaptureItem As String = "{79c3f95b-31f7-4ec2-a464-632ef5d30760}"
' ----==== Enums ====----
Private Enum vtb_Interfaces
....
' IGraphicsCaptureItemInterop
IGraphicsCaptureItemInterop_CreateForWindow = 3
IGraphicsCaptureItemInterop_CreateForMonitor = 4
End Enum
Private m_pIGraphicsCaptureItemInterop As Long
' ----==== Events ====----
' Variable to hold 'ChoseCaptureFormHwnd' property value
Private m_bChoseCaptureFormHwnd As Boolean
Public Property Get ChoseCaptureFormHwnd() As Boolean
ChoseCaptureFormHwnd = m_bChoseCaptureFormHwnd
End Property
Public Property Let ChoseCaptureFormHwnd(ByVal bValue As Boolean)
m_bChoseCaptureFormHwnd = bValue
End Property
' ----==== Class ====----
Private Sub Class_Initialize()
m_bolIsInitialized = False
m_bolIsCapturePickerCreated = False
m_bChoseCaptureFormHwnd = False
End Sub
Public Function Init()
Dim tGdipStartupInput As GDIPlusStartupInput
Dim tGdipStartupOutput As GdiplusStartupOutput
If m_bChoseCaptureFormHwnd = False Then
Dim pIGraphicsCaptureSessionStatics As Long
If GetActivationFactory(WindowsGraphicsCaptureGraphicsCaptureSession, IID_IGraphicsCaptureSessionStatics, pIGraphicsCaptureSessionStatics) Then
Dim bolIsSupported As Boolean
If Invoke(pIGraphicsCaptureSessionStatics, IGraphicsCaptureSessionStatics_IsSupported, VarPtr(bolIsSupported)) = S_OK Then
If bolIsSupported Then
If GetActivateInstance(WindowsGraphicsCaptureGraphicsCapturePicker, IID_IGraphicsCapturePicker, m_pIGraphicsCapturePicker) Then
If m_bolIsInitialized = False Then
tGdipStartupInput.GdiPlusVersion = GdiPlusVersion
If GdiplusStartup(m_lngGdipToken, tGdipStartupInput, tGdipStartupOutput) = OK Then
m_bolIsInitialized = True
End If
End If
End If
End If
End If
Call Release(pIGraphicsCaptureSessionStatics)
End If
Else
If GetActivationFactory(WindowsGraphicsCaptureGraphicsCaptureItem, IID_IGraphicsCaptureItemInterop, m_pIGraphicsCaptureItemInterop) Then
' Dim bolIsSupported As Boolean
' If Invoke(m_pIGraphicsCaptureItemInterop, _
' IGraphicsCaptureSessionStatics_IsSupported, _
' VarPtr(bolIsSupported)) = S_OK Then
' If bolIsSupported Then
'' If GetActivateInstance(WindowsGraphicsCaptureGraphicsCapturePicker, _
'' IID_IGraphicsCapturePicker, _
'' m_pIGraphicsCapturePicker) Then
If m_bolIsInitialized = False Then
tGdipStartupInput.GdiPlusVersion = GdiPlusVersion
If GdiplusStartup(m_lngGdipToken, tGdipStartupInput, tGdipStartupOutput) = OK Then
m_bolIsInitialized = True
'
End If
End If
' End If
' End If
' End If
'Call Release(m_pIGraphicsCaptureItemInterop)
End If
End If
End Function
Public Function CaptureHwnd(ByVal hwnd As Long) As Boolean
If m_pIGraphicsCaptureItemInterop <> 0& And hwnd <> 0& Then
Dim pIGraphicsCaptureItem As Long
If Invoke(m_pIGraphicsCaptureItemInterop, IGraphicsCaptureItemInterop_CreateForWindow, hwnd, VarPtr(Str2Guid(IID_IGraphicsCaptureItem)), VarPtr(pIGraphicsCaptureItem)) = S_OK Then
Call StartCapture(pIGraphicsCaptureItem)
CaptureHwnd = True
Call Release(pIGraphicsCaptureItem)
End If
End If
End Function
Code:
VERSION 5.00
Begin VB.Form frmMain
Caption = "Form1"
ClientHeight = 7320
ClientLeft = 60
ClientTop = 408
ClientWidth = 9180
LinkTopic = "Form1"
ScaleHeight = 610
ScaleMode = 3 'Pixel
ScaleWidth = 765
StartUpPosition = 3 '窗口缺省
Begin VB.TextBox txtHwnd
Height = 372
Left = 7080
TabIndex = 3
Top = 240
Width = 732
End
Begin VB.CommandButton Command2
Caption = "capture hwnd"
Height = 552
Left = 4200
TabIndex = 2
Top = 120
Width = 2628
End
Begin VB.PictureBox Picture1
Height = 6375
Left = 120
ScaleHeight = 6324
ScaleWidth = 8820
TabIndex = 1
Top = 720
Width = 8865
End
Begin VB.CommandButton Command1
Caption = "Show CapturePicker"
Height = 555
Left = 150
TabIndex = 0
Top = 120
Width = 3945
End
End
Attribute VB_Name = "frmMain"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
' Autor: F. Sch黮er ([email protected])
' Datum: 08/2022
Option Explicit
Private cCapturePicker As clsCapturePicker
Private Sub Command2_Click()
cCapturePicker.ChoseCaptureFormHwnd = True
cCapturePicker.Init
If cCapturePicker.CaptureHwnd(Val(txtHwnd.Text)) Then
If Not cCapturePicker.CapturePicture Is Nothing Then
Picture1.Picture = cCapturePicker.CapturePicture
Else
Picture1.Picture = LoadPicture()
End If
Else
Picture1.Picture = LoadPicture()
End If
End Sub
Private Sub Form_Load()
Command1.Enabled = False
Set cCapturePicker = New clsCapturePicker
cCapturePicker.ChoseCaptureFormHwnd = False
cCapturePicker.Init
If cCapturePicker.IsInitialized Then
If cCapturePicker.CreateCapturePicker(Me.hwnd) Then
Command1.Enabled = True
End If
End If
txtHwnd.Text = Me.hwnd
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
Set cCapturePicker = Nothing
End Sub
Private Sub Command1_Click()
If cCapturePicker.PickSingleItemAsync Then
If Not cCapturePicker.CapturePicture Is Nothing Then
Picture1.Picture = cCapturePicker.CapturePicture
Else
Picture1.Picture = LoadPicture()
End If
Else
Picture1.Picture = LoadPicture()
End If
End Sub
These function calls create a GDI "hBitmap" from the pixel data provided by "tD3D11_MAPPED_SUBRESOURCE.pData". I have tried to replace them by:
Code:
Dim hDC As Long, hBitmap As Long, bmiBitmapInfo As BITMAPINFO, lRet As Long
hDC = GetDC(0)
hBitmap = CreateCompatibleBitmap(hDC, tD3D11_TEXTURE2D_DESC.Width, tD3D11_TEXTURE2D_DESC.Height)
hDC = ReleaseDC(0, hDC)
With bmiBitmapInfo.bmiHeader
.biSize = LenB(bmiBitmapInfo.bmiHeader): .biPlanes = 1: .biBitCount = 32: .biCompression = BI_RGB
.biWidth = tD3D11_TEXTURE2D_DESC.Width: .biHeight = -tD3D11_TEXTURE2D_DESC.Height
.biSizeImage = (((.biWidth * .biBitCount) + 31) \ 32) * 4 * tD3D11_TEXTURE2D_DESC.Height
End With
lRet = SetDIBits(0, hBitmap, 0, tD3D11_TEXTURE2D_DESC.Height, ByVal tD3D11_MAPPED_SUBRESOURCE.pData, bmiBitmapInfo, DIB_RGB_COLORS)
This works to some extent, as in I do end up with a "hBitmap" containing the source pixels, however the image is all garbled as if the pixels are somehow misaligned in memory.
I have a feeling that the parameter "tD3D11_MAPPED_SUBRESOURCE.RowPitch" might have something to do with this but I don't see how I can incorporate that into the call to "SetDIBits". I have read the "Pitch" remarks from the documentation on D3D11_MAPPED_SUBRESOURCE but I still don't understand what's really going on under the covers.
Maybe someone with more experience in GDI Bitmaps can chime in with some advice?
Last edited by VanGoghGaming; Oct 13th, 2023 at 04:49 PM.
Re: Problems getting a window capture with Bitblt and PrintWindow.
I've been working on making a WinRT typelib/tB interface package... The stumbling block isn't HSTRING; that's straightforward. The issue I have is some of these insane things that look like dynamically defined interface templates.
Re: Problems getting a window capture with Bitblt and PrintWindow.
Originally Posted by fafalone
The issue I have is some of these insane things that look like dynamically defined interface templates.
I can understand you well that this is not easy to implement. I also sometimes have problems with various WinRT interfaces and don't know how to approach them. Unfortunately, what is very easy in .Net cannot always be replicated in VB6.
Re: Problems getting a window capture with Bitblt and PrintWindow.
This is what I mean. The code captures the whole window (Form1.hWnd) and puts the screenshot in the Picturebox below the two buttons. The first "hBitmap" (made with GDI+) is displayed correctly and the second one (made with SetDIBits) is displayed all warped. I'll be damned if I know what's causing it, the code above looks spot-on whichever way I look at it...
Last edited by VanGoghGaming; Oct 13th, 2023 at 04:49 PM.
Re: Problems getting a window capture with Bitblt and PrintWindow.
Originally Posted by VanGoghGaming
The first "hBitmap" (made with GDI+) is displayed correctly and the second one (made with SetDIBits) is displayed all warped
tD3D11_MAPPED_SUBRESOURCE.RowPitch gives you back the stride value. For 32bpp this would typically be = Width * 4. The D3D11_MAPPED_SUBRESOURCE documentation says:
For D3D_FEATURE_LEVEL_10_0 and higher, the pointer is aligned to 16 bytes. For lower than D3D_FEATURE_LEVEL_10_0, the pointer is aligned to 4 bytes.
You calculate the stride value yourself here:
.biSizeImage = (((.biWidth * .biBitCount) + 31) \ 32) * 4 * tD3D11_TEXTURE2D_DESC.Height
Try the following:
.biSizeImage = tD3D11_MAPPED_SUBRESOURCE.RowPitch * tD3D11_TEXTURE2D_DESC.Height
Re: Problems getting a window capture with Bitblt and PrintWindow.
Originally Posted by -Franky-
tD3D11_MAPPED_SUBRESOURCE.RowPitch gives you back the stride value. For 32bpp this would typically be = Width * 4. The D3D11_MAPPED_SUBRESOURCE documentation says:
For D3D_FEATURE_LEVEL_10_0 and higher, the pointer is aligned to 16 bytes. For lower than D3D_FEATURE_LEVEL_10_0, the pointer is aligned to 4 bytes.
Yeah I've already read that paragraph in MSDN 3 times and it still doesn't make much sense. I have no idea what's a "stride". Also tD3D11_TEXTURE2D_DESC.Width is 621 for this screenshot and 621*4=2484 while tD3D11_MAPPED_SUBRESOURCE.RowPitch=2560 so I don't know where is this difference coming from, maybe some sort of padding? If so then how can you remove it?
You calculate the stride value yourself here:
.biSizeImage = (((.biWidth * .biBitCount) + 31) \ 32) * 4 * tD3D11_TEXTURE2D_DESC.Height
Try the following:
.biSizeImage = tD3D11_MAPPED_SUBRESOURCE.RowPitch * tD3D11_TEXTURE2D_DESC.Height
Unfortunately this also doesn't work (it would have been too easy if it did, haha)! Furthermore the documentation about BITMAPINFOHEADER says that:
Code:
biSizeImage
Specifies the size, in bytes, of the image. This can be set to 0 for uncompressed RGB bitmaps.
And that seems to be correct, setting "biSizeImage" to zero makes absolutely no difference...
I have a feeling the solution to this issue should be rather easy and it's still eluding us...
Re: Problems getting a window capture with Bitblt and PrintWindow.
Originally Posted by VanGoghGaming
...I have no idea what's a "stride".
It's an info-attribute, related to making "looped copies".
(representing the pointer-offset to add, before you start your next loop over zerobased x-Values in the current row)
When it is larger than the ImageWidth (or in case of "RowPitch" larger than Width*BytesPerPixel),
then you have to make a copy in a loop (Row-by-Row).
Either via CopyMemory (into a truly consecutive DIB-allocation first, before you Blit-Out your DIB) -
or (in case you want to avoid an intermediate DIB-allocation) by Blitting directly in that loop (defining your DIB to be only "one Row in Height").
Olaf
Last edited by Schmidt; Apr 22nd, 2023 at 01:27 PM.
Re: Problems getting a window capture with Bitblt and PrintWindow.
Thanks for explaining how stuff works, Olaf. You rock as usual! I've finally managed to get it right with the following code using CopyMemory to an intermediate array:
Code:
Dim bytePixelData() As Byte
ReDim bytePixelData(0 To tD3D11_TEXTURE2D_DESC.Width * tD3D11_TEXTURE2D_DESC.Height * 4 - 1)
With bmiBitmapInfo.bmiHeader
.biSize = LenB(bmiBitmapInfo.bmiHeader): .biPlanes = 1: .biBitCount = 32: .biCompression = BI_RGB
.biWidth = tD3D11_TEXTURE2D_DESC.Width: .biHeight = -tD3D11_TEXTURE2D_DESC.Height
End With
For i = 0 To tD3D11_TEXTURE2D_DESC.Height - 1
CopyMemory bytePixelData(i * tD3D11_TEXTURE2D_DESC.Width * 4), ByVal tD3D11_MAPPED_SUBRESOURCE.pData + i * tD3D11_MAPPED_SUBRESOURCE.RowPitch, tD3D11_TEXTURE2D_DESC.Width * 4
Next i
SetDIBits 0, hBitmap, 0, tD3D11_TEXTURE2D_DESC.Height, bytePixelData(0), bmiBitmapInfo, DIB_RGB_COLORS
However the following code doesn't work as expected:
Code:
For i = 0 To tD3D11_TEXTURE2D_DESC.Height - 1
SetDIBits 0, hBitmap, i, 1, ByVal tD3D11_MAPPED_SUBRESOURCE.pData + i * tD3D11_MAPPED_SUBRESOURCE.RowPitch, bmiBitmapInfo, DIB_RGB_COLORS
Next i
Is it not possible to blit one row at a time in a "hBitmap" with "SetDIBits"?
Last edited by VanGoghGaming; Oct 13th, 2023 at 04:49 PM.
Re: Problems getting a window capture with Bitblt and PrintWindow.
Originally Posted by VanGoghGaming
Is it not possible to blit one row at a time in a "hBitmap" with "SetDIBits"?
It isn't, ... but you get similar performance when you use StretchDIBits instead:
- without Stretching anything
- but with RowIndex i in the DstY-Param
- the number of Rows set to 1
- and BIHeader.biHeight Member also at 1 (or -1)
Re: Problems getting a window capture with Bitblt and PrintWindow.
Actually it seems "SetDIBits" does work with one row at a time, but blitting needed to start in reversed order. This took a LOT of "trial and error" but the following code works perfectly:
Code:
For i = 0 To tD3D11_TEXTURE2D_DESC.Height - 1
SetDIBits 0, hBitmap, tD3D11_TEXTURE2D_DESC.Height - i - 1, 1, ByVal tD3D11_MAPPED_SUBRESOURCE.pData + i * tD3D11_MAPPED_SUBRESOURCE.RowPitch, bmiBitmapInfo, DIB_RGB_COLORS
Next i
CopyMemory to an intermediate array no longer needed!
Last edited by VanGoghGaming; Oct 13th, 2023 at 04:50 PM.