# Visual Basic > Visual Basic 6 and Earlier >  Problems getting a window capture with Bitblt and PrintWindow.

## Maatooh

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!.

----------


## -Franky-

Hi
From Windows 10 you can also use the UWP/WinRT namespace Windows.Graphics.Capture (https://learn.microsoft.com/en-us/uw...ew=winrt-22621) in VB6 . Try if you can make a screenshot with this. https://www.activevb.de/cgi-bin/uplo...oad.pl?id=3904

----------


## wqweto

@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?

cheers,
</wqw>

----------


## xxdoc123

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

----------


## -Franky-

@wqweto Sorry for the links to the ZIP download. I will revise my code again briefly and then upload a new ZIP here.

Edit: In the ActiveVB Up/Downlod I also have a code for WinRT JSON. Find the VBC_UwpJson.zip there.

----------


## VanGoghGaming

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.

----------


## wqweto

> Edit: In the ActiveVB Up/Downlod I also have a code for WinRT JSON. Find the VBC_UwpJson.zip there.


Thanks! Looking at it now.

Btw, in ToString impl is there WindowsDeleteString call missing on the output hString?

cheers,
</wqw>

----------


## VanGoghGaming

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)_:



```
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.

----------


## Maatooh

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!  :big yellow:

----------


## VanGoghGaming

> 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"!

----------


## -Franky-

> Btw, in ToString impl is there WindowsDeleteString call missing on the output hString?
> </wqw>


If I read that correctly in the MS Docs, hString must be deleted via WindowsDeleteString if it was created with WindowsCreateString. Otherwise not.

----------


## wqweto

> 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>

----------


## -Franky-

> 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.

----------


## VanGoghGaming

@Franky is it possible to start the capture directly from a supplied window hWnd instead of displaying that "CapturePicker" window every time?

----------


## -Franky-

Hi
In the windows.graphics.capture.interop.h there is a function CreateForWindow.


```
HRESULT CreateForWindow(
  HWND   window,
  REFIID riid,
  void   **result
);
```

REFIID = IID_IGraphicsCaptureItem

Edit: add the following


```
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
```

----------


## 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:

https://www.autohotkey.com/boards/vi...yle=17&t=96161

The similarities are uncanny, almost as if it's the same code translated in VB6!  :Big Grin:

----------


## -Franky-

> 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:
> ...


Caught. Let's put it that way. I copied a few things from this AutoHotKey Script and some comes directly from Microsoft examples.  :Roll Eyes (Sarcastic): 


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.

----------


## VanGoghGaming

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)...

----------


## xxdoc123

The code should be sent to codebank

BitBlt cannot work with background windows!

----------


## VanGoghGaming

> 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:


```
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:


```
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.  :Big Grin:

----------


## -Franky-

> The code should be sent to codebank


You can do whatever you want with the WinRT code. I make no claim to it. I'm just playing around with WinRT to explore some possibilities for VB6.

----------


## xxdoc123

> 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:
> 
> 
> ```
> Option Explicit
> ...


from1 and from2 in the same exe .

if you test form2 in other exe ，you will see  different

----------


## VanGoghGaming

> 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):

----------


## xxdoc123

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 ~

----------


## xxdoc123

> Hi
> In the windows.graphics.capture.interop.h there is a function CreateForWindow.
> 
> 
> ```
> HRESULT CreateForWindow(
>   HWND   window,
>   REFIID riid,
>   void   **result
> ...


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 .

----------


## beetecaste

Thank you very much!! -Franky-, VanGoghGaming. The solutions are excellent for this problem.!

----------

