|
-
Apr 24th, 2023, 10:49 PM
#11
Re: Problems getting a window capture with Bitblt and PrintWindow.
@Franky - I've managed to optimize your CapturePicker class to achieve decent capturing speeds now. Stripped it down to the bare minimum required to capture either a whole monitor or a specific window and replaced some "Invokes" with direct calls from a D3D11 TypeLib. It turns out much of the code needs to be executed only once, not every time a capture is taken. That was killing the capture speed before!
frmMain.frm
Code:
Option Explicit
Private Sub cmdCaptureMonitor_Click()
StartTiming
If clsCapturePicker.CaptureMonitor() Then
Set picCapture.Picture = clsCapturePicker.Picture
Else
Set picCapture.Picture = LoadPicture()
End If
lblTiming = Elapsed
End Sub
Private Sub cmdCaptureWindow_Click()
clsCapturePicker.hWnd = Me.hWnd
StartTiming
If clsCapturePicker.CaptureWindow() Then
Set picCapture.Picture = clsCapturePicker.Picture
Else
Set picCapture.Picture = LoadPicture()
End If
lblTiming = Elapsed
End Sub
Private Sub Form_Load()
QueryPerformanceFrequency cFrequency
Me.Show
clsCapturePicker.SelectMonitorFromPoint 1920, 0
End Sub
mdlTiming.bas
Code:
Option Explicit
Public seqStart As Currency, seqStop As Currency, cFrequency As Currency
Public Sub StartTiming()
QueryPerformanceCounter seqStart
End Sub
Public Function Elapsed() As Double
QueryPerformanceCounter seqStop
Elapsed = (seqStop - seqStart) * 1000 / cFrequency
End Function
clsCapturePicker.cls
Code:
Option Explicit
Private Const IID_IPicture As String = "{7bf80980-bf32-101a-8bbb-00aa00300cab}"
Private Const IID_IClosable As String = "{30d5a829-7fa4-4026-83bb-d75bae4ea99e}"
Private Const IID_IDXGIDevice As String = "{54ec77fa-1377-44e6-8c32-88fd5f44c84c}"
Private Const IID_IDirect3DDevice As String = "{a37624ab-8d5f-4650-9d3e-9eae3d9bc670}"
Private Const WindowsGraphicsCaptureGraphicsCaptureSession As String = "Windows.Graphics.Capture.GraphicsCaptureSession"
Private Const IID_IGraphicsCaptureSession As String = "{814e42a9-f70f-4ad7-939b-fddcc6eb880d}"
Private Const IID_IGraphicsCaptureSession_2 As String = "{2c39ae40-7d2e-5044-804e-8b6799d4cf9e}"
Private Const IID_IGraphicsCaptureSession_3 As String = "{f2cdd966-22ae-5ea1-9596-3a289344c3be}"
Private Const IID_IGraphicsCaptureSessionStatics As String = "{2224a540-5974-49aa-b232-0882536f4cb5}"
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 Const WindowsGraphicsCaptureDirect3D11CaptureFramePool As String = "Windows.Graphics.Capture.Direct3D11CaptureFramePool"
Private Const IID_IDirect3D11CaptureFramePoolStatics As String = "{7784056a-67aa-4d53-ae54-1088d5a8ca21}"
Private Const IID_ID3D11Texture2D As String = "{6f15aaf2-d208-4e89-9ab4-489535d34f9c}"
Private Const IID_IDirect3DDxgiInterfaceAccess As String = "{a9b3d012-3df2-4ee3-b8d1-8695f457d3c1}"
Private Enum vtb_Interfaces
' IUnknown
IUnknown_QueryInterface = 0
IUnknown_Release = 2
' IClosable
IClosable_Close = 6
' IGraphicsCaptureSessionStatics
IGraphicsCaptureSessionStatics_IsSupported = 6
' IGraphicsCaptureItem
IGraphicsCaptureItem_GetDisplayName = 6
IGraphicsCaptureItem_GetSize = 7
' IDirect3D11CaptureFramePoolStatics
IDirect3D11CaptureFramePoolStatics_Create = 6
' IDirect3D11CaptureFramePool
IDirect3D11CaptureFramePool_TryGetNextFrame = 7
IDirect3D11CaptureFramePool_CreateCaptureSession = 10
' IGraphicsCaptureSession
IGraphicsCaptureSession_StartCapture = 6
' IGraphicsCaptureSession2
IGraphicsCaptureSession2_GetIsCursorCaptureEnabled = 6
IGraphicsCaptureSession2_PutIsCursorCaptureEnabled = 7
' IGraphicsCaptureSession3
IGraphicsCaptureSession3_GetIsBorderRequired = 6
IGraphicsCaptureSession3_PutIsBorderRequired = 7
' IDirect3D11CaptureFrame
IDirect3D11CaptureFrame_GetSurface = 6
' IDirect3DDxgiInterfaceAccess
IDirect3DDxgiInterfaceAccess_GetInterface = 3
' ID3D11Texture2D
ID3D11Texture2D_GetDesc = 10
' ID3D11Device
ID3D11Device_CreateTexture2D = 5
' ID3D11DeviceContext
ID3D11DeviceContext_Map = 14
ID3D11DeviceContext_Unmap = 15
ID3D11DeviceContext_CopyResource = 47
' IGraphicsCaptureItemInterop
IGraphicsCaptureItemInterop_CreateForWindow = 3
IGraphicsCaptureItemInterop_CreateForMonitor = 4
End Enum
Private Type MONITORINFO
cbSize As Long
rcMonitor As RECT
rcWork As RECT
dwFlags As Long
End Type
Private Declare Function vbaCastObj Lib "msvbvm60" Alias "__vbaCastObj" (ByVal pObj As Long, ByVal pIID As Long) As IUnknown
Private Declare Function DispCallFunc Lib "oleaut32" (ByVal pvInstance As Long, ByVal oVft As Long, ByVal cc As Long, ByVal vtReturn As VbVarType, ByVal cActuals As Long, prgvt As Any, prgpvarg As Any, pvargResult As Variant) As Long
Private Declare Function OleCreatePictureIndirect Lib "oleaut32" (ByVal lpPictDesc As Long, ByVal riid As Long, ByVal fOwn As Long, lplpvObj As IPicture) As Long
Private Declare Function IIDFromString Lib "combase" (ByVal lpsz As Long, lpiid As Any) As Long
Private Declare Function WindowsCreateString Lib "combase" (ByVal sourceString As Long, ByVal length As Long, hString As Long) As Long
Private Declare Function WindowsDeleteString Lib "combase" (ByVal sourceString As Long) As Long
Private Declare Function RoGetActivationFactory Lib "combase" (ByVal activatableClassId As Long, ByVal riid As Long, pFactory As Long) As Long
Private Declare Function GetMonitorInfo Lib "user32" Alias "GetMonitorInfoW" (ByVal hMonitor As Long, ByVal lpmi As Long) As Long
Private Declare Function MonitorFromPoint Lib "user32" (ByVal X As Long, ByVal Y As Long, ByVal dwFlags As Long) As Long
Private Declare Function CreateDirect3D11DeviceFromDXGIDevice Lib "d3d11" (ByVal dxgiDevice As Long, graphicsDevice As Long) As Long
Private IIDs(0 To 51) As Long, pIID_IClosable As Long, pIID_IDXGIDevice As Long, pIID_IDirect3DDevice As Long, pIID_IGraphicsCaptureSession As Long, pIID_IGraphicsCaptureSession_2 As Long, _
pIID_IGraphicsCaptureSessionStatics As Long, pIID_IGraphicsCaptureItemInterop As Long, pIID_IGraphicsCaptureItem As Long, pIID_IDirect3D11CaptureFramePoolStatics As Long, _
pIID_ID3D11Texture2D As Long, pIID_IPicture As Long, pIID_IDirect3DDxgiInterfaceAccess As Long, pIID_IGraphicsCaptureSession_3 As Long, pIID_IGraphicsCaptureAccessStatics As Long
Private m_Picture As IPicture, m_bIsInitialized As Boolean, m_hWnd As Long, m_hMonitor As Long, m_lWidth As Long, m_lHeight As Long, m_hDC As Long, m_hBitmap As Long, _
m_BitmapInfo As BITMAPINFO, m_PicDesc As PICTDESCBMP
Private m_pIGraphicsCaptureItemInterop As Long, objID3D11Device As VBD3D11.ID3D11Device, objID3D11DeviceContext As VBD3D11.ID3D11DeviceContext, objIDXGIDevice As VBD3D11.IDXGIDevice, _
pIDirect3D11CaptureFramePoolStatics As Long, pIInspectable As Long, objIDirect3DDevice As IUnknown, pIGraphicsCaptureWnd As Long, pIGraphicsCaptureMonitor As Long
Private Sub Class_Initialize()
IIDFromString StrPtr(IID_IClosable), IIDs(0): pIID_IClosable = VarPtr(IIDs(0))
IIDFromString StrPtr(IID_IDXGIDevice), IIDs(4): pIID_IDXGIDevice = VarPtr(IIDs(4))
IIDFromString StrPtr(IID_IDirect3DDevice), IIDs(8): pIID_IDirect3DDevice = VarPtr(IIDs(8))
IIDFromString StrPtr(IID_IGraphicsCaptureSession), IIDs(12): pIID_IGraphicsCaptureSession = VarPtr(IIDs(12))
IIDFromString StrPtr(IID_IGraphicsCaptureSession_2), IIDs(16): pIID_IGraphicsCaptureSession_2 = VarPtr(IIDs(16))
IIDFromString StrPtr(IID_IGraphicsCaptureSession_3), IIDs(20): pIID_IGraphicsCaptureSession_3 = VarPtr(IIDs(20))
IIDFromString StrPtr(IID_IGraphicsCaptureSessionStatics), IIDs(24): pIID_IGraphicsCaptureSessionStatics = VarPtr(IIDs(24))
IIDFromString StrPtr(IID_IGraphicsCaptureItemInterop), IIDs(28): pIID_IGraphicsCaptureItemInterop = VarPtr(IIDs(28))
IIDFromString StrPtr(IID_IGraphicsCaptureItem), IIDs(32): pIID_IGraphicsCaptureItem = VarPtr(IIDs(32))
IIDFromString StrPtr(IID_IDirect3D11CaptureFramePoolStatics), IIDs(36): pIID_IDirect3D11CaptureFramePoolStatics = VarPtr(IIDs(36))
IIDFromString StrPtr(IID_ID3D11Texture2D), IIDs(40): pIID_ID3D11Texture2D = VarPtr(IIDs(40))
IIDFromString StrPtr(IID_IDirect3DDxgiInterfaceAccess), IIDs(44): pIID_IDirect3DDxgiInterfaceAccess = VarPtr(IIDs(44))
IIDFromString StrPtr(IID_IPicture), IIDs(48): pIID_IPicture = VarPtr(IIDs(48))
m_BitmapInfo.bmiHeader.biSize = LenB(m_BitmapInfo.bmiHeader): m_BitmapInfo.bmiHeader.biPlanes = 1: m_BitmapInfo.bmiHeader.biBitCount = 32
m_PicDesc.cbSizeofstruct = LenB(m_PicDesc): m_PicDesc.picType = vbPicTypeBitmap: m_hDC = GetDC(0)
Dim pIGraphicsCaptureSessionStatics As Long
If GetActivationFactory(WindowsGraphicsCaptureGraphicsCaptureSession, pIID_IGraphicsCaptureSessionStatics, pIGraphicsCaptureSessionStatics) Then
Dim bIsSupported As Boolean
If Invoke(pIGraphicsCaptureSessionStatics, IGraphicsCaptureSessionStatics_IsSupported, VarPtr(bIsSupported)) = S_OK Then
If bIsSupported Then
If GetActivationFactory(WindowsGraphicsCaptureGraphicsCaptureItem, pIID_IGraphicsCaptureItemInterop, m_pIGraphicsCaptureItemInterop) Then
If GetActivationFactory(WindowsGraphicsCaptureDirect3D11CaptureFramePool, pIID_IDirect3D11CaptureFramePoolStatics, pIDirect3D11CaptureFramePoolStatics) Then
If VBD3D11.D3D11CreateDevice(Nothing, D3D_DRIVER_TYPE_HARDWARE, 0, D3D11_CREATE_DEVICE_BGRA_SUPPORT, ByVal 0&, 0, D3D11_SDK_VERSION, objID3D11Device, 0, objID3D11DeviceContext) = S_OK Then
Set objIDXGIDevice = vbaCastObj(ObjPtr(objID3D11Device), pIID_IDXGIDevice)
If Not (objIDXGIDevice Is Nothing) Then
If CreateDirect3D11DeviceFromDXGIDevice(ObjPtr(objIDXGIDevice), pIInspectable) = S_OK Then
Set objIDirect3DDevice = vbaCastObj(pIInspectable, pIID_IDirect3DDevice)
End If
End If
End If
End If
End If
End If
End If
Call Release(pIGraphicsCaptureSessionStatics)
End If
End Sub
Private Sub Class_Terminate()
If m_hDC Then m_hDC = ReleaseDC(0, m_hDC)
If m_hBitmap Then m_hBitmap = DeleteObject(m_hBitmap)
Call CloseAndRelease(pIInspectable)
Set objIDirect3DDevice = Nothing: Set objIDXGIDevice = Nothing
Call Release(pIDirect3D11CaptureFramePoolStatics)
Call Release(pIGraphicsCaptureWnd): Call Release(pIGraphicsCaptureMonitor)
Call Release(m_pIGraphicsCaptureItemInterop)
End Sub
Friend Property Get IsInitialized() As Boolean
IsInitialized = m_bIsInitialized
End Property
Friend Property Get Picture() As IPicture
Call OleCreatePictureIndirect(VarPtr(m_PicDesc), pIID_IPicture, APITRUE, m_Picture)
Set Picture = m_Picture
End Property
Friend Property Get hBitmap() As Long
hBitmap = m_hBitmap
End Property
Friend Property Get hWnd(Optional bOverwriteWnd As Boolean) As Long
hWnd = m_hWnd
End Property
Friend Property Let hWnd(Optional bOverwriteWnd As Boolean, lWnd As Long)
Dim rcWndRect As RECT
If IsWindow(lWnd) Then
If Not bOverwriteWnd Then If m_hWnd = lWnd Then Exit Property
If Not IsMinimized(lWnd) Then
m_hWnd = lWnd
GetWindowRect m_hWnd, rcWndRect
With rcWndRect: m_lWidth = .Right - .Left: m_lHeight = .bottom - .Top: End With
If m_hBitmap Then DeleteObject m_hBitmap
m_hBitmap = CreateCompatibleBitmap(m_hDC, m_lWidth, m_lHeight): m_PicDesc.hBitmap = m_hBitmap
With m_BitmapInfo.bmiHeader: .biWidth = m_lWidth: .biHeight = -m_lHeight: End With
If pIGraphicsCaptureWnd Then Call Release(pIGraphicsCaptureWnd)
If Invoke(m_pIGraphicsCaptureItemInterop, IGraphicsCaptureItemInterop_CreateForWindow, m_hWnd, pIID_IGraphicsCaptureItem, VarPtr(pIGraphicsCaptureWnd)) = S_OK Then m_bIsInitialized = True
End If
End If
End Property
Friend Function CaptureWindow() As Boolean
If m_hWnd Then If m_bIsInitialized Then If Not IsMinimized(m_hWnd) Then CaptureWindow = StartCapture(pIGraphicsCaptureWnd)
End Function
Friend Function CaptureMonitor() As Boolean
If m_hMonitor Then If m_bIsInitialized Then CaptureMonitor = StartCapture(pIGraphicsCaptureMonitor)
End Function
Friend Sub SelectMonitorFromPoint(Optional X As Long, Optional Y As Long)
Dim mi As MONITORINFO
m_hMonitor = MonitorFromPoint(X, Y, 2): mi.cbSize = LenB(mi): GetMonitorInfo m_hMonitor, VarPtr(mi)
With mi.rcMonitor: m_lWidth = .Right - .Left: m_lHeight = .bottom - .Top: End With
If m_hBitmap Then DeleteObject m_hBitmap
m_hBitmap = CreateCompatibleBitmap(m_hDC, m_lWidth, m_lHeight): m_PicDesc.hBitmap = m_hBitmap
With m_BitmapInfo.bmiHeader: .biWidth = m_lWidth: .biHeight = -m_lHeight: End With
If pIGraphicsCaptureMonitor Then Call Release(pIGraphicsCaptureMonitor)
If Invoke(m_pIGraphicsCaptureItemInterop, IGraphicsCaptureItemInterop_CreateForMonitor, m_hMonitor, pIID_IGraphicsCaptureItem, VarPtr(pIGraphicsCaptureMonitor)) = S_OK Then m_bIsInitialized = True
End Sub
Private Function StartCapture(ByVal pIGraphicsCaptureItem As Long) As Boolean
Dim pIDirect3D11CaptureFramePool As Long
If Invoke(pIDirect3D11CaptureFramePoolStatics, IDirect3D11CaptureFramePoolStatics_Create, ObjPtr(objIDirect3DDevice), DXGI_FORMAT_B8G8R8A8_UNORM, 1, m_lWidth, m_lHeight, VarPtr(pIDirect3D11CaptureFramePool)) = S_OK Then
Dim pIGraphicsCaptureSession As Long
If Invoke(pIDirect3D11CaptureFramePool, IDirect3D11CaptureFramePool_CreateCaptureSession, pIGraphicsCaptureItem, VarPtr(pIGraphicsCaptureSession)) = S_OK Then
Dim pIGraphicsCaptureSession2 As Long
If Invoke(pIGraphicsCaptureSession, IUnknown_QueryInterface, pIID_IGraphicsCaptureSession_2, VarPtr(pIGraphicsCaptureSession2)) = S_OK Then
Call Invoke(pIGraphicsCaptureSession2, IGraphicsCaptureSession2_PutIsCursorCaptureEnabled, 0&)
Call Release(pIGraphicsCaptureSession2)
End If
Dim pIGraphicsCaptureSession3 As Long
If Invoke(pIGraphicsCaptureSession, IUnknown_QueryInterface, pIID_IGraphicsCaptureSession_3, VarPtr(pIGraphicsCaptureSession3)) = S_OK Then
Call Invoke(pIGraphicsCaptureSession3, IGraphicsCaptureSession3_PutIsBorderRequired, 0&)
Call Release(pIGraphicsCaptureSession3)
End If
If Invoke(pIGraphicsCaptureSession, IGraphicsCaptureSession_StartCapture) = S_OK Then
Dim pIDirect3D11CaptureFrame As Long
While pIDirect3D11CaptureFrame = 0: Call Invoke(pIDirect3D11CaptureFramePool, IDirect3D11CaptureFramePool_TryGetNextFrame, VarPtr(pIDirect3D11CaptureFrame)): Wend
Dim pIDirect3DSurface As Long
If Invoke(pIDirect3D11CaptureFrame, IDirect3D11CaptureFrame_GetSurface, VarPtr(pIDirect3DSurface)) = S_OK Then
StartCapture = GetImageFromIDirect3DSurface(pIDirect3DSurface)
Call CloseAndRelease(pIDirect3DSurface)
End If
Call CloseAndRelease(pIDirect3D11CaptureFrame)
End If
Call CloseAndRelease(pIGraphicsCaptureSession)
End If
Call CloseAndRelease(pIDirect3D11CaptureFramePool)
End If
End Function
Private Function GetImageFromIDirect3DSurface(pIDirect3DSurface As Long) As Boolean
Dim pIDirect3DDxgiInterfaceAccess As Long
If Invoke(pIDirect3DSurface, IUnknown_QueryInterface, pIID_IDirect3DDxgiInterfaceAccess, VarPtr(pIDirect3DDxgiInterfaceAccess)) = S_OK Then
Dim objID3D11Texture2D As VBD3D11.ID3D11Texture2D
If Invoke(pIDirect3DDxgiInterfaceAccess, IDirect3DDxgiInterfaceAccess_GetInterface, pIID_ID3D11Texture2D, VarPtr(objID3D11Texture2D)) = S_OK Then
Dim tD3D11_TEXTURE2D_DESC As VBD3D11.D3D11_TEXTURE2D_DESC
objID3D11Texture2D.GetDesc tD3D11_TEXTURE2D_DESC
With tD3D11_TEXTURE2D_DESC
.Usage = D3D11_USAGE_STAGING: .CPUAccessFlags = D3D11_CPU_ACCESS_READ: .BindFlags = 0: .MiscFlags = 0
End With
Dim objID3D11Texture2D_2 As VBD3D11.ID3D11Texture2D
Set objID3D11Texture2D_2 = objID3D11Device.CreateTexture2D(tD3D11_TEXTURE2D_DESC, ByVal 0&)
If Not (objID3D11Texture2D_2 Is Nothing) Then
Call objID3D11DeviceContext.CopyResource(objID3D11Texture2D_2, objID3D11Texture2D)
Dim tD3D11_MAPPED_SUBRESOURCE As VBD3D11.D3D11_MAPPED_SUBRESOURCE, i As Long
If objID3D11DeviceContext.Map(objID3D11Texture2D_2, 0, D3D11_MAP_READ, 0, tD3D11_MAPPED_SUBRESOURCE) = S_OK Then
With tD3D11_MAPPED_SUBRESOURCE
If m_lWidth * 4 = .RowPitch Then
SetDIBits 0, m_hBitmap, 0, m_lHeight, ByVal .pData, m_BitmapInfo, DIB_RGB_COLORS
Else
Dim PixelData() As Long
ReDim PixelData(0 To m_lWidth * m_lHeight - 1)
For i = 0 To m_lHeight - 1
CopyMemory PixelData(i * m_lWidth), ByVal .pData + i * .RowPitch, m_lWidth * 4
Next i
SetDIBits 0, m_hBitmap, 0, m_lHeight, PixelData(0), m_BitmapInfo, DIB_RGB_COLORS
End If
End With
objID3D11DeviceContext.Unmap objID3D11Texture2D_2, 0: GetImageFromIDirect3DSurface = True
End If
End If
End If
Call Release(pIDirect3DDxgiInterfaceAccess)
End If
End Function
Private Function IsMinimized(lWnd As Long) As Boolean
IsMinimized = GetWindowLong(lWnd, GWL_STYLE) And WS_MINIMIZE
End Function
Private Function GetActivationFactory(ByVal ClassName As String, ByVal iid As Long, pFactory As Long) As Boolean
Dim hString As Long
If WindowsCreateString(StrPtr(ClassName), Len(ClassName), hString) = S_OK Then
If hString Then
If RoGetActivationFactory(hString, iid, pFactory) = S_OK Then GetActivationFactory = True
Call WindowsDeleteString(hString)
End If
End If
End Function
Private Sub Release(pInterface As Long)
If pInterface Then
Call Invoke(pInterface, IUnknown_Release)
pInterface = 0
End If
End Sub
Private Sub CloseAndRelease(pInterface As Long)
If pInterface Then
Dim pIClosable As Long
If Invoke(pInterface, IUnknown_QueryInterface, pIID_IClosable, VarPtr(pIClosable)) = S_OK Then
Call Invoke(pIClosable, IClosable_Close)
Call Release(pIClosable)
End If
Call Release(pInterface)
End If
End Sub
Private Function Invoke(ByVal pInterface As Long, ByVal vtb As vtb_Interfaces, ParamArray aParam()) As Variant
Dim i As Long, ParamValues(0 To 9) As Long, ParamTypes(0 To 9) As Integer, varParam As Variant, varRet As Variant
If pInterface Then
varParam = aParam
For i = 0 To UBound(varParam)
ParamTypes(i) = VarType(varParam(i))
ParamValues(i) = VarPtr(varParam(i))
Next i
Call DispCallFunc(pInterface, vtb * 4, CC_STDCALL, vbLong, i, ParamTypes(0), ParamValues(0), varRet)
Invoke = varRet
End If
End Function
Here's a screenshot captured from my second monitor (12.5ms for a FullHD screenshot 1920x1080 including the conversion to GDI Bitmap):

And the demo project: VBC_UwpCapturePicker.zip, requires the VBD3D11 TypeLib for the D3D11 calls and Bruce McKinney's Windows Unicode API Type Library for the rest of API functions, types and constants.
Last edited by VanGoghGaming; Oct 13th, 2023 at 04:50 PM.
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|