This is a work in progress of a remote control utility. This is the screen capturing part using DirectX 11 (DXGI).
There is a custom DirectX 11 type library (both .idl and .tlb in the archive) with just enough interfaces to instantiate IDXGIOutputDuplication and capture a texture which is then converted to a DIB which is then converted to a StdPicture and placed in a stretching Image control so the scale quality is poor.
The idea is for a remote screen sharing implementation to transport only screen diffs using GetFrameDirtyRects, GetFrameMoveRects and GetFramePointerShape methods (instead of current full screen capture) with some fast LZ4 compression on top and some Foreward Error Correction implementation over UDP, including UDP hole punching for serverless peer-to-peer connections when both parties happen to be behind NAT or alternative is using STUN/TURN infrastructure as currently provided by google for WebRTC.
cheers,
</wqw>
Code:
Option Explicit
DefObj A-Z
#Const SHOW_DELTA = False
#Const STRETCH_POINTER = False
'=========================================================================
' API
'=========================================================================
'--- DIB Section constants
Private Const DIB_RGB_COLORS As Long = 0 ' color table in RGBs
'--- for OpenInputDesktop
Private Const GENERIC_READ As Long = &H80000000
'--- for SetProcessDpiAwarenessContext
Private Const DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 As Long = -4
'--- for D3DKMTSetProcessSchedulingPriorityClass
Private Const D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME As Long = 5
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDst As Any, lpSrc As Any, ByVal ByteLength As Long)
Private Declare Function CreateDIBSection Lib "gdi32" (ByVal hDC As Long, lpBitsInfo As BITMAPINFOHEADER, ByVal wUsage As Long, lpBits As Long, ByVal Handle As Long, ByVal dw As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hDC As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hDC As Long) As Long
Private Declare Function OleCreatePictureIndirect Lib "oleaut32" (lpPictDesc As PICTDESC, riid As Any, ByVal fPictureOwnsHandle As Long, ipic As IPicture) As Long
Private Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As Currency) As Long
Private Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As Currency) As Long
Private Declare Function OpenInputDesktop Lib "user32" (ByVal dwFlags As Long, ByVal fInherit As Boolean, ByVal dwDesiredAccess As Long) As Long
Private Declare Function CloseDesktop Lib "user32" (ByVal hDesktop As Long) As Long
Private Declare Function SetThreadDesktop Lib "user32" (ByVal hDesktop As Long) As Long
Private Declare Function SetProcessDpiAwarenessContext Lib "user32" (ByVal lValue As Long) As Long
Private Declare Function D3DKMTSetProcessSchedulingPriorityClass Lib "gdi32" (ByVal hProcess As Long, ByVal lPriority As Long) As Long
Private Declare Function GetCurrentProcess Lib "kernel32" () As Long
Private Type BITMAPINFOHEADER
biSize As Long
biWidth As Long
biHeight As Long
biPlanes As Integer
biBitCount As Integer
biCompression As Long
biSizeImage As Long
biXPelsPerMeter As Long
biYPelsPerMeter As Long
biClrUsed As Long
biClrImportant As Long
End Type
Private Type PICTDESC
lSize As Long
lType As Long
hBmp As Long
hPal As Long
End Type
'=========================================================================
' Constants and mamber variables
'=========================================================================
Private m_uCtx As UcsDuplicationContext
Private m_uFrame As UcsCaptureFrame
Private Type UcsDuplicationContext
DeviceName As String
Width As Long
Height As Long
Timeout As Long
Context As ID3D11DeviceContext
Duplication As IDXGIOutputDuplication
StageTexture As ID3D11Texture2D
DesktopResource As ID3D11Resource
InSystemMemory As Boolean
Pitch As Long
Stride As Long
DesktopPicture As StdPicture
DesktopBitsPtr As Long
PointerPicture As StdPicture
PointerBitsPtr As Long
End Type
Private Type UcsCaptureFrame
NumMoveRects As Long
MoveRects() As DXGI_OUTDUPL_MOVE_RECT
NumDirtyRects As Long
DirtyRects() As D3D11_RECT
PointerSize As Long
PointerShape() As Byte
PointerVisible As Boolean
PointerPlacement As D3D11_RECT
PointerHotspot As D3D11_POINT
End Type
'=========================================================================
' Error handling
'=========================================================================
Private Sub PrintError(sFuncName As String)
Debug.Print Err.Description & " in " & Err.Source, sFuncName
If MsgBox(Err.Description & " in " & Err.Source, vbCritical Or vbOKCancel, sFuncName) = vbCancel Then
Unload Me
End If
End Sub
'=========================================================================
' Methods
'=========================================================================
Private Function pvEnumOutputDeviceNames() As Collection
Dim aGUID(0 To 3) As Long
Dim pFactory As IDXGIFactory1
Dim lIdx As Long
Dim lJdx As Long
Dim pAdapter As IDXGIAdapter1
Dim pOutput As IDXGIOutput1
Dim uAdapterDesc As DXGI_ADAPTER_DESC
Dim uOutputDesc As DXGI_OUTPUT_DESC
Set pvEnumOutputDeviceNames = New Collection
Call IIDFromString(szIID_DXGIFactory1, aGUID(0))
Set pFactory = CreateDXGIFactory1(aGUID(0))
For lIdx = 0 To 100
Set pAdapter = Nothing
If pFactory.EnumAdapters1(lIdx, pAdapter) < 0 Then
Exit For
End If
pAdapter.GetDesc uAdapterDesc
' Debug.Print Replace(uAdapterDesc.Description, vbNullChar, vbNullString)
For lJdx = 0 To 100
Set pOutput = Nothing
If pAdapter.EnumOutputs(lJdx, pOutput) < 0 Then
Exit For
End If
pOutput.GetDesc uOutputDesc
pvEnumOutputDeviceNames.Add Array(Replace(uOutputDesc.DeviceName, vbNullChar, vbNullString), _
Replace(uAdapterDesc.Description, vbNullChar, vbNullString))
Next
Next
End Function
Private Function pvInitCapture(uCtx As UcsDuplicationContext, ByVal sDeviceName As String, ByVal lTimeout As Long) As Boolean
Const FUNC_NAME As String = "pvInitCapture"
Dim hDesktop As Long
Dim aGUID(0 To 3) As Long
Dim pFactory As IDXGIFactory1
Dim lIdx As Long
Dim lJdx As Long
Dim pAdapter As IDXGIAdapter1
Dim pOutput As IDXGIOutput1
Dim pOutput5 As IDXGIOutput5
Dim uOutputDesc As DXGI_OUTPUT_DESC
Dim hResult As Long
Dim pD3D11Device As ID3D11Device
Dim pDXGIDevice As IDXGIDevice1
Dim uTextureDesc As D3D11_TEXTURE2D_DESC
Dim uDuplDesc As DXGI_OUTDUPL_DESC
Dim uResource As D3D11_MAPPED_SUBRESOURCE
On Error GoTo EH
'--- allow capture the secure desktop
hDesktop = OpenInputDesktop(0, 0, GENERIC_READ)
If hDesktop <> 0 Then
Call SetThreadDesktop(hDesktop)
Call CloseDesktop(hDesktop)
End If
On Error Resume Next '--- Windows 10, version 1703 and above
Call SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
On Error GoTo EH
With uCtx
.DeviceName = vbNullString
Set .DesktopResource = Nothing
Set .Duplication = Nothing
Set .StageTexture = Nothing
Set .Context = Nothing
Set .DesktopPicture = Nothing
Set .PointerPicture = Nothing
Call IIDFromString(szIID_DXGIFactory1, aGUID(0))
Set pFactory = CreateDXGIFactory1(aGUID(0))
For lIdx = 0 To 100
Set pAdapter = Nothing
hResult = pFactory.EnumAdapters1(lIdx, pAdapter)
If hResult = DXGI_ERROR_NOT_FOUND Then
Exit For
End If
If hResult < 0 Then
Err.Raise hResult, "IDXGIFactory1.EnumAdapters1"
End If
For lJdx = 0 To 100
Set pOutput = Nothing
hResult = pAdapter.EnumOutputs(lJdx, pOutput)
If hResult = DXGI_ERROR_NOT_FOUND Then
Exit For
End If
If hResult < 0 Then
Err.Raise hResult, "IDXGIAdapter1.EnumOutputs"
End If
pOutput.GetDesc uOutputDesc
If LenB(sDeviceName) <> 0 And Not Replace(uOutputDesc.DeviceName, vbNullChar, vbNullString) Like sDeviceName Then
GoTo Continue
End If
If uOutputDesc.AttachedToDesktop <> 0 Then
lIdx = 100
Exit For
End If
Continue:
Next
Next
If pOutput Is Nothing Then
GoTo QH
End If
.DeviceName = Replace(uOutputDesc.DeviceName, vbNullChar, vbNullString)
.Width = uOutputDesc.DesktopCoordinates.Right - uOutputDesc.DesktopCoordinates.Left
.Height = uOutputDesc.DesktopCoordinates.Bottom - uOutputDesc.DesktopCoordinates.Top
.Timeout = lTimeout
hResult = D3D11CreateDevice(pAdapter, D3D_DRIVER_TYPE_UNKNOWN, 0, D3D11_CREATE_DEVICE_VIDEO_SUPPORT, ByVal 0, 0, D3D11_SDK_VERSION, pD3D11Device, 0, .Context)
If hResult < 0 Then
Err.Raise hResult, "D3D11CreateDevice"
End If
Call D3DKMTSetProcessSchedulingPriorityClass(GetCurrentProcess(), D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME)
Set pDXGIDevice = pD3D11Device
pDXGIDevice.SetGPUThreadPriority 7
pDXGIDevice.SetMaximumFrameLatency 1
If TypeOf pOutput Is IDXGIOutput5 Then
Set pOutput5 = pOutput
Dim aFormats(0 To 3) As DXGI_FORMAT
aFormats(0) = DXGI_FORMAT_B8G8R8A8_UNORM
aFormats(1) = DXGI_FORMAT_R8G8B8A8_UNORM
aFormats(2) = DXGI_FORMAT_R10G10B10A2_UNORM
aFormats(3) = DXGI_FORMAT_R16G16B16A16_FLOAT
hResult = pOutput5.DuplicateOutput1(pD3D11Device, 0, UBound(aFormats) + 1, aFormats(0), .Duplication)
If hResult < 0 Then
Err.Raise hResult, "IDXGIOutput5.DuplicateOutput1"
End If
Else
hResult = pOutput.DuplicateOutput(pD3D11Device, .Duplication)
If hResult < 0 Then
Err.Raise hResult, "IDXGIOutput1.DuplicateOutput"
End If
End If
.Duplication.GetDesc uDuplDesc
.InSystemMemory = (uDuplDesc.DesktopImageInSystemMemory <> 0)
Debug.Assert uDuplDesc.ModeDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM
With uTextureDesc
.Width = uCtx.Width
.Height = uCtx.Height
.MipLevels = 1
.ArraySize = 1
.Format = uDuplDesc.ModeDesc.Format
.SampleDesc.Count = 1
.SampleDesc.Quality = 0
.Usage = D3D11_USAGE_STAGING
.BindFlags = 0
.CPUAccessFlags = D3D11_CPU_ACCESS_READ
.MiscFlags = 0
End With
Set .StageTexture = pD3D11Device.CreateTexture2D(uTextureDesc)
hResult = .Context.Map(.StageTexture, 0, D3D11_MAP_READ, 0, uResource)
If hResult < 0 Then
Err.Raise hResult, "ID3D11DeviceContext.Map"
End If
.Pitch = uResource.RowPitch
.Stride = uResource.RowPitch / IIf(uDuplDesc.ModeDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT, 8, 4)
.Context.Unmap .StageTexture, 0
End With
'--- success
pvInitCapture = True
QH:
Exit Function
EH:
PrintError FUNC_NAME
End Function
Private Function pvCaptureScreen(uCtx As UcsDuplicationContext, oPicDesktop As StdPicture, oPicPointer As StdPicture, uCapture As UcsCaptureFrame) As Boolean
Const FUNC_NAME As String = "pvCaptureScreen"
Const SIZE_OUTDUPL_MOVE_RECT As Long = 24
Const SIZE_RECT As Long = 16
Const BLACK_COLOR As Long = &HFF000000
Dim hResult As Long
Dim lIdx As Long
Dim uResource As D3D11_MAPPED_SUBRESOURCE
Dim hMemDC As Long
Dim hDib As Long
Dim uMapRect As DXGI_MAPPED_RECT
Dim lSize As Long
Dim dblTimerEx As Double
Dim lX As Long
Dim lY As Long
Dim pTex As ID3D11Texture2D
Dim uFrameInfo As DXGI_OUTDUPL_FRAME_INFO
Dim aMask(0 To 7) As Byte
Dim uPointerInfo As DXGI_OUTDUPL_POINTER_SHAPE_INFO
On Error GoTo EH
dblTimerEx = TimerEx
With uCtx
If .Duplication Is Nothing Then
GoTo QH
End If
If Not .DesktopResource Is Nothing Then
.Duplication.ReleaseFrame
Set .DesktopResource = Nothing
End If
hResult = .Duplication.AcquireNextFrame(1, uFrameInfo, .DesktopResource)
If hResult = DXGI_ERROR_WAIT_TIMEOUT Then
'--- success
pvCaptureScreen = True
GoTo QH
End If
If hResult < 0 Then
GoTo QH
End If
If uFrameInfo.LastPresentTime.LowPart <> 0 Or uFrameInfo.LastPresentTime.HighPart <> 0 Then
Set pTex = .StageTexture
End If
'--- init mem dc
hMemDC = CreateCompatibleDC(0)
If hMemDC = 0 Then
GoTo QH
End If
'--- capture frame
hResult = .Duplication.GetFrameMoveRects((UBound(uCapture.MoveRects) + 1) * SIZE_OUTDUPL_MOVE_RECT, uCapture.MoveRects(0), lSize)
If hResult = DXGI_ERROR_MORE_DATA Then
ReDim uCapture.MoveRects(0 To lSize \ SIZE_OUTDUPL_MOVE_RECT - 1) As DXGI_OUTDUPL_MOVE_RECT
hResult = .Duplication.GetFrameMoveRects((UBound(uCapture.MoveRects) + 1) * SIZE_OUTDUPL_MOVE_RECT, uCapture.MoveRects(0), lSize)
End If
If hResult < 0 Then
GoTo QH
End If
uCapture.NumMoveRects = lSize / SIZE_OUTDUPL_MOVE_RECT
hResult = .Duplication.GetFrameDirtyRects((UBound(uCapture.DirtyRects) + 1) * SIZE_RECT, uCapture.DirtyRects(0), lSize)
If hResult = DXGI_ERROR_MORE_DATA Then
ReDim uCapture.DirtyRects(0 To lSize \ SIZE_RECT - 1) As D3D11_RECT
hResult = .Duplication.GetFrameDirtyRects((UBound(uCapture.DirtyRects) + 1) * SIZE_RECT, uCapture.DirtyRects(0), lSize)
End If
If hResult < 0 Then
GoTo QH
End If
uCapture.NumDirtyRects = lSize / SIZE_RECT
If uFrameInfo.PointerShapeBufferSize > 0 Then
hResult = .Duplication.GetFramePointerShape((UBound(uCapture.PointerShape) + 1), uCapture.PointerShape(0), uCapture.PointerSize, uPointerInfo)
If hResult = DXGI_ERROR_MORE_DATA Then
ReDim uCapture.PointerShape(0 To uCapture.PointerSize - 1) As Byte
hResult = .Duplication.GetFramePointerShape((UBound(uCapture.PointerShape) + 1), uCapture.PointerShape(0), uCapture.PointerSize, uPointerInfo)
End If
If hResult < 0 Then
GoTo QH
End If
uCapture.PointerHotspot = uPointerInfo.HotSpot
End If
If uFrameInfo.LastMouseUpdateTime.LowPart <> 0 Or uFrameInfo.LastMouseUpdateTime.HighPart <> 0 Then
uCapture.PointerVisible = (uFrameInfo.PointerPosition.Visible <> 0)
uCapture.PointerPlacement.Left = uFrameInfo.PointerPosition.Position.X
uCapture.PointerPlacement.Top = uFrameInfo.PointerPosition.Position.Y
End If
'--- copy desktop
If .DesktopPicture Is Nothing Then
If Not pvCreateDib(hMemDC, .Width, .Height, hDib, .DesktopBitsPtr) Then
GoTo QH
End If
If Not pvCreateStdPicture(hDib, .DesktopPicture) Then
GoTo QH
End If
hDib = 0
Set oPicDesktop = .DesktopPicture
End If
If .InSystemMemory Then
.Duplication.MapDesktopSurface uMapRect
For lIdx = 0 To .Height - 1
Call CopyMemory(ByVal .DesktopBitsPtr + lIdx * .Width * 4, ByVal uMapRect.pBitsPtr + lIdx * uMapRect.Pitch, .Width * 4)
Next
.Duplication.UnMapDesktopSurface
ElseIf Not pTex Is Nothing Then
.Context.CopyResource pTex, .DesktopResource
hResult = .Context.Map(pTex, 0, D3D11_MAP_READ, 0, uResource)
If hResult < 0 Then
Err.Raise hResult, "ID3D11DeviceContext.Map"
End If
#If SHOW_DELTA Then
For lIdx = 0 To .Height - 1
Call CopyMemory(ByVal .DesktopBitsPtr + lIdx * .Width * 4, ByVal uResource.pDataPtr + lIdx * uResource.RowPitch, .Width * 4)
Next
Const BORDER_COLOR As Long = &HFFFF0000
For lIdx = 0 To uCapture.NumDirty - 1
For lX = uCapture.DirtyRects(lIdx).Left To uCapture.DirtyRects(lIdx).Right - 1
Call CopyMemory(ByVal .DesktopBitsPtr + (uCapture.DirtyRects(lIdx).Top * .Width + lX) * 4, BORDER_COLOR, 4)
Call CopyMemory(ByVal .DesktopBitsPtr + ((uCapture.DirtyRects(lIdx).Bottom - 1) * .Width + lX) * 4, BORDER_COLOR, 4)
Next
For lY = uCapture.DirtyRects(lIdx).Top To uCapture.DirtyRects(lIdx).Bottom - 1
Call CopyMemory(ByVal .DesktopBitsPtr + (lY * .Width + uCapture.DirtyRects(lIdx).Left) * 4, BORDER_COLOR, 4)
Call CopyMemory(ByVal .DesktopBitsPtr + (lY * .Width + uCapture.DirtyRects(lIdx).Right - 1) * 4, BORDER_COLOR, 4)
Next
Next
#Else
For lIdx = 0 To uCapture.NumDirtyRects - 1
lX = uCapture.DirtyRects(lIdx).Left
For lY = uCapture.DirtyRects(lIdx).Top To uCapture.DirtyRects(lIdx).Bottom - 1
Call CopyMemory(ByVal .DesktopBitsPtr + (lY * .Width + lX) * 4, ByVal uResource.pDataPtr + lY * uResource.RowPitch + lX * 4, (uCapture.DirtyRects(lIdx).Right - lX) * 4)
Next
Next
#End If
.Context.Unmap pTex, 0
uResource.pDataPtr = 0
End If
'--- copy pointer
If uFrameInfo.PointerShapeBufferSize > 0 Then
If uPointerInfo.Type <> DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR Then
uPointerInfo.Height = uPointerInfo.Height \ 2
End If
If Not pvCreateDib(hMemDC, uPointerInfo.Width, uPointerInfo.Height, hDib, .PointerBitsPtr) Then
GoTo QH
End If
If Not pvCreateStdPicture(hDib, .PointerPicture) Then
GoTo QH
End If
hDib = 0
Set oPicPointer = .PointerPicture
Select Case uPointerInfo.Type
Case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR
For lY = 0 To uPointerInfo.Height - 1
Call CopyMemory(ByVal .PointerBitsPtr + lY * uPointerInfo.Width * 4, uCapture.PointerShape(lY * uPointerInfo.Pitch), uPointerInfo.Width * 4)
Next
Case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME
For lIdx = 0 To 7
aMask(lIdx) = &H80 \ 2 ^ (lIdx Mod 8)
Next
'--- collect XOR mask only (skip AND)
lIdx = uPointerInfo.Pitch * uPointerInfo.Height
For lY = 0 To uPointerInfo.Height - 1
For lX = 0 To uPointerInfo.Width - 1
If (uCapture.PointerShape(lIdx + lY * uPointerInfo.Pitch + lX \ 8) And aMask(lX Mod 8)) <> 0 Then
Call CopyMemory(ByVal .PointerBitsPtr + (lY * uPointerInfo.Width + lX) * 4, BLACK_COLOR, 4)
End If
Next
Next
Case Else
Debug.Print ".PointerInfo.Type=" & Hex(uPointerInfo.Type)
End Select
uCapture.PointerPlacement.Right = uPointerInfo.Width
uCapture.PointerPlacement.Bottom = uPointerInfo.Height
End If
End With
'--- success
pvCaptureScreen = True
QH:
If hDib <> 0 Then
Call DeleteObject(hDib)
hDib = 0
End If
If hMemDC <> 0 Then
Call DeleteDC(hMemDC)
hMemDC = 0
End If
If uResource.pDataPtr <> 0 Then
uCtx.Context.Unmap pTex, 0
End If
If Not pTex Is Nothing Then
Debug.Print "Elapsed=" & Format(TimerEx - dblTimerEx, "0.000")
End If
Exit Function
EH:
PrintError FUNC_NAME
Resume QH
End Function
Private Function pvCreateDib(ByVal hMemDC As Long, ByVal lWidth As Long, ByVal lHeight As Long, hDib As Long, Optional lpBits As Long) As Boolean
Const FUNC_NAME As String = "pvCreateDib"
Dim uHdr As BITMAPINFOHEADER
On Error GoTo EH
With uHdr
.biSize = Len(uHdr)
.biPlanes = 1
.biBitCount = 32
.biWidth = lWidth
.biHeight = -lHeight
.biSizeImage = 4 * lWidth * lHeight
End With
hDib = CreateDIBSection(hMemDC, uHdr, DIB_RGB_COLORS, lpBits, 0, 0)
If hDib = 0 Then
GoTo QH
End If
'--- success
pvCreateDib = True
QH:
Exit Function
EH:
PrintError FUNC_NAME
Resume QH
End Function
Private Function pvCreateStdPicture(hDib As Long, oPic As StdPicture) As Boolean
Const FUNC_NAME As String = "pvCreateStdPicture"
Dim uDesc As PICTDESC
Dim aGUID(0 To 3) As Long
On Error GoTo EH
With uDesc
.lSize = Len(uDesc)
.lType = vbPicTypeBitmap
.hBmp = hDib
End With
'--- IID_IPicture
aGUID(0) = &H7BF80980
aGUID(1) = &H101ABF32
aGUID(2) = &HAA00BB8B
aGUID(3) = &HAB0C3000
If OleCreatePictureIndirect(uDesc, aGUID(0), 1, oPic) <> 0 Then
GoTo QH
End If
'--- success
pvCreateStdPicture = True
QH:
Exit Function
EH:
PrintError FUNC_NAME
End Function
Public Property Get TimerEx() As Double
Dim cFreq As Currency
Dim cValue As Currency
Call QueryPerformanceFrequency(cFreq)
Call QueryPerformanceCounter(cValue)
TimerEx = cValue / cFreq
End Property
'=========================================================================
' Control events
'=========================================================================
Private Sub Form_Load()
Dim vElem As Variant
With m_uFrame
ReDim .MoveRects(0 To 0) As DXGI_OUTDUPL_MOVE_RECT
ReDim .DirtyRects(0 To 0) As D3D11_RECT
ReDim .PointerShape(0 To 0) As Byte
End With
For Each vElem In pvEnumOutputDeviceNames
Combo1.AddItem vElem(0)
Next
Combo1.ListIndex = 0
End Sub
Private Sub Form_Resize()
Dim dblTop As Double
If WindowState <> vbMinimized Then
dblTop = Combo1.Top + Combo1.Height + Combo1.Top
If ScaleHeight - dblTop > 0 Then
imgDesktop.Move 0, dblTop, ScaleWidth, ScaleHeight - dblTop
End If
End If
End Sub
Private Sub Combo1_Click()
If Combo1.ListIndex >= 0 Then
If Not pvInitCapture(m_uCtx, Combo1.Text, Timer1.Interval) Then
Timer1.Enabled = False
Else
Timer1.Enabled = True
Timer1_Timer
End If
End If
End Sub
Private Sub imgDesktop_Click()
Timer1.Enabled = Not Timer1.Enabled
End Sub
...
The idea is for a remote screen sharing implementation to transport only screen diffs using GetFrameDirtyRects, GetFrameMoveRects and GetFramePointerShape methods (instead of current full screen capture) with some fast LZ4 compression on top and some Foreward Error Correction implementation over UDP, including UDP hole punching for serverless peer-to-peer connections when both parties happen to be behind NAT or alternative is using STUN/TURN infrastructure as currently provided by google for WebRTC.
cheers,
</wqw>