• Welcome to Overclockers Forums! Join us to reply in threads, receive reduced ads, and to customize your site experience!

print screen from vba

Overclockers is supported by our readers. When you click a link to make a purchase, we may earn a commission. Learn More.

jmh547

Member
Joined
Jul 26, 2005
I am writing a VBA program in access that opens a JD Edwards window pulls data to insert into a table, printscreen and save to a file, and then moves on to the next window. Everything is working, however, the only way I could find to copy the window is use keybd_event. When I initially ran this the clipboard was empty, I finally figured out that the code was moving forward before the printscreen was complete so I put a short timer in. My problem with this timer is that it isn't fool proof. If the computer is bogged down with background tasks it can take longer than the 2 seconds I have in the code. On the other hand I am running through hundreds of screens so I want this timer to be a small as possible. Does anyone have a better way of verifying that the print screen is complete before allowing the code to move forward?


Below is the code to clear the clipboard, copy the window to the clipboard, and the timer.
Code:
'Clear Clipboard
    If OpenClipboard(0&) <> 0 Then
        Call EmptyClipboard
        Call CloseClipboard
    End If
    
'Copy Window
    osinfo.dwOSVersionInfoSize = 148
    osinfo.szCSDVersion = Space$(128)
    retvalue = GetVersionExA(osinfo)
    
    If osinfo.dwMajorVersion > 4 Then
        blnAboveVer4 = True
    Else
        blnAboveVer4 = False
    End If
    
    If blnAboveVer4 Then
        keybd_event VK_SNAPSHOT, 1, 0, 0
    Else
        keybd_event VK_MENU, 0, 0, 0
        keybd_event VK_SNAPSHOT, 0, 0, 0
        keybd_event VK_SNAPSHOT, 0, KEYEVENTF_KEYUP, 0
        keybd_event VK_MENU, 0, KEYEVENTF_KEYUP, 0
    End If

'Give keybd_event time to fill clipboard
    Wait = Now + TimeValue("00:00:02")
      
    Do
        DoEvents
    Loop Until Now >= Wait
 
A timer is not the way to go...too many variables that can make you miss data.

I also would not trap into a keyboard event...it will be asynchronous to your main code (i.e. you don't know when it will be executed.)

When you say "printscreen and save to a file" I am assuming you mean create a bitmap of the window...like when I do CTRL+ALT+Printscreen?


You should be able to link to a clipboard object:


Dim DataObj As New MSForms.DataObject

'Put a string in the clipboard
DataObj.SetText "Overclockers!"
DataObj.PutInClipboard


It looks like the ".PutInClipboard" is a blocking call.


Another option
 
A timer is not the way to go...too many variables that can make you miss data.

I also would not trap into a keyboard event...it will be asynchronous to your main code (i.e. you don't know when it will be executed.)

When you say "printscreen and save to a file" I am assuming you mean create a bitmap of the window...like when I do CTRL+ALT+Printscreen?


You should be able to link to a clipboard object:


Dim DataObj As New MSForms.DataObject

'Put a string in the clipboard
DataObj.SetText "Overclockers!"
DataObj.PutInClipboard


It looks like the ".PutInClipboard" is a blocking call.


Another option

Jr thanks for the input. I started coding something similar to what you suggested (take the text on the screen and create an image from it). I then had a thought, if I can somehow verify the status of the clipboard rather than the status of the printscreen. So my friend Google and I came up with the following:

Code:
cnt = 0
Do While IsClipboardFormatAvailable(CF_BITMAP) = False And cnt < 200
    DoEvents
    cnt = cnt + 1
Loop

In a nutshell it loops until the clipboard contains a bitmap (or cnt>=200 just so it does not get stuck in a infinite loop).

I just ran a bunch of tests and could not get it to fail once. I think I got it.
 
I believe that the .PutInClipboard method is a blocking call. If that is the case, there is no need to put a loop in like that. Just put an error check to make sure that the data is there. Does VBA support a "Try" / "Catch" for error trapping?

Loops that wait for something to finish is not "clean" programming - hehe.

If you keep the loop, you need to research the "IsClipboardFormatAvailable" method and verify that it is "thread safe". Said differently, your loop is reading this value very quickly on thread A. If the ".PutInClipboard" is not a blocking call (i.e. you would need to loop), then this is running in thread B. Your code does not use a "lock" statement (don't know if VBA supports this), so unless the "IsClipboardFormatAvailable" is thread safe, you can have 2 different threads trying to read and write the same item at the same time...producing potentially unstable results.

Do the research on ".PutInClipboard"...if it's a blocking call, then no worries and no need to do the loop! :thup:
 
I believe that the .PutInClipboard method is a blocking call. If that is the case, there is no need to put a loop in like that. Just put an error check to make sure that the data is there. Does VBA support a "Try" / "Catch" for error trapping?

Loops that wait for something to finish is not "clean" programming - hehe.

If you keep the loop, you need to research the "IsClipboardFormatAvailable" method and verify that it is "thread safe". Said differently, your loop is reading this value very quickly on thread A. If the ".PutInClipboard" is not a blocking call (i.e. you would need to loop), then this is running in thread B. Your code does not use a "lock" statement (don't know if VBA supports this), so unless the "IsClipboardFormatAvailable" is thread safe, you can have 2 different threads trying to read and write the same item at the same time...producing potentially unstable results.

Do the research on ".PutInClipboard"...if it's a blocking call, then no worries and no need to do the loop! :thup:

I believe the VBA equivalent to Try/Catch is On Error Goto label#

I wont disagree it is not clean...

Ok my head is spinning :LOL: I am not sure if IsClipboardFormatAvailable is thread safe... I did a quick google but I have not clue what I am looking for. I am not using the .PutInClipboard (still using the keybd_event) because I really need an image of the screen, this if for proof the that the data in the database has not been manipulated. The only way I see .PutInClipboard would work is to copy the text from the window and put the text in the window.
 
Have you ever looked at the Win32 BitBlt function? You have to add this to your file:


Declare Function BitBlt Lib "gdi32.dll" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long


This is how you copy the screen to the clipboard using this:



'Screen Capture Procedure, coordinates are expressed in pixels
Public Sub CaptureScreen(Left As Long, Top As Long, Width As Long, Height As Long)

Dim srcDC As Long
Dim trgDC As Long
Dim BMPHandle As Long
Dim dm As DEVMODE

srcDC = CreateDC("DISPLAY", "", "", dm)
trgDC = CreateCompatibleDC(srcDC)
BMPHandle = CreateCompatibleBitmap(srcDC, Width, Height)

SelectObject trgDC, BMPHandle
BitBlt trgDC, 0, 0, Width, Height, srcDC, Left, Top, SRCCOPY

OpenClipboard 0&
EmptyClipboard
SetClipboardData 2, BMPHandle
CloseClipboard
DeleteDC trgDC

ReleaseDC BMPHandle, srcDC​

End Sub



Each is a blocking call, so you do not have to worry about looping
 
Back