# VBForums CodeBank > CodeBank - Visual Basic .NET >  VB.NET - Screenshot to jpeg...

## Libero

Im shure this will do the trick for many people.

VB Code:
Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Integer, ByVal dwExtraInfo As Integer)
    Private Const VK_SNAPSHOT As Short = &H2Cs
    
    
    Public Function SaveScreen(ByVal theFile As String) As Boolean
        Dim data As IDataObject
        data = Clipboard.GetDataObject()
        Dim bmap As Bitmap
        If data.GetDataPresent(GetType(System.Drawing.Bitmap)) Then
            bmap = CType(data.GetData(GetType(System.Drawing.Bitmap)), Bitmap)
            Me.PictureBox1.Image = bmap
            Me.PictureBox1.Image.Save(theFile, Imaging.ImageFormat.Jpeg)
        End If
    End Function
     Private Sub Command2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Command2.Click
        Call keybd_event(System.Windows.Forms.Keys.Snapshot, 0, 0, 0)
        System.Threading.Thread.Sleep(200) ' To have time to catch the clipboard
        SaveScreen("c:\test.jpg")
    End Sub

----------


## Liquid Metal

With all respect, how come the jpeg is kind of fuzzy?

----------


## Libero

It might be that a jpeg file is a compressed bmp file and therefore might the compression corrupt the file a little bit. If you want to look deeper into this you will have to search for it. I am shure you can choose how much compression you want with coding, but for now the quality of the screenshot will due for me.  :wave:

----------


## dynamic_sysop

> _Originally posted by Liquid Metal_ 
> *With all respect, how come the jpeg is kind of fuzzy?*


when the image opens ( usually in Windows PictureViewer ) , try clicking the actuall size button at the bottom  :Wink:   the picture should then be clear ( you are seeing a compressed version because it wont show as actuall size by default due to the size of a screenshot )

----------


## tr333

wouldnt it be better to save to a PNG file if its a screenshot?

----------


## wossname

This is a little smoother, and ts 100% framework (no API)...


VB Code:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
         If Not SaveScreen(Application.StartupPath & "\screen.png") Then
            MessageBox.Show("Sorry dude, couldn't do it.")
        End If
     End Sub
     Public Function SaveScreen(ByVal theFile As String) As Boolean
         Try
            SendKeys.Send("%{PRTSC}")            '<alt + printscreen>
            Application.DoEvents()
             Dim data As IDataObject = Clipboard.GetDataObject()
            
            If data.GetDataPresent(GetType(System.Drawing.Bitmap)) Then
                Dim bmp As Bitmap = CType(data.GetData(GetType(System.Drawing.Bitmap)), Bitmap)
                bmp.Save(theFile, Imaging.ImageFormat.Png)
            End If
            Clipboard.SetDataObject(0)        'save memory by removing the image from the clipboard
            Return True
        Catch ex As Exception
            Return False
        End Try
     End Function

----------


## NSiggel

Follow up to WOSSMAN code.

works great except I keep getting a problem after doing about 8-10 screenshots ! I get an Exception error.

Further debugging reveals it happens during the .Save command
The Err.Message is
"Object reference not set to an instance of an object" 

I would appreciate any feedback since I would love to use this really nice and compact code snippet to do screenshot capture, however I have to capture approximately 100-4000 Screenshots over the span of 60-120minutes.

Any suggestions would be welcome.
Sincerely

----------


## wossname

> Follow up to WOSSMAN code.
> 
> works great except I keep getting a problem after doing about 8-10 screenshots ! I get an Exception error.
> 
> Further debugging reveals it happens during the .Save command
> The Err.Message is
> "Object reference not set to an instance of an object" 
> 
> I would appreciate any feedback since I would love to use this really nice and compact code snippet to do screenshot capture, however I have to capture approximately 100-4000 Screenshots over the span of 60-120minutes.
> ...


Wow, that's a lot of screenshots, worst case scenario by my calculations means 1.1 screenies per second.  That is probably pushing it a bit.  The thing about printscreen is that it is not a high-priority feature.  If you want to take many snaps, then you would be much better off using the API.  You would have much better performance (you would probably be able to do 2 or 3 snaps per second, depending on how fast you can save the data).  But it would be at the cost of complexity.  API would be a little harder to code, but it's probably worth it.

I don't have any samples for you at the moment, but the API functions you should check out are:

*GetDC*
*BitBlt* (The old favourite  :Big Grin: )

If you have API-Guide installed you can get some examples off there.

Hope that helps.

PS< Can you post the code that caused your error?

----------


## FishGuy

I have looked at this thread and it seems that it could solve one of my problems but I need it to work as a windows service or at least in the background so the user is unaware it is running.  the main problems seem to be caused by the send keys command and the IData objects not been recognised in services. Any Ideas.

----------


## FishGuy

Ok Ihave added a reference to system.windows.forms which has solved my Clipboard problems i think, I just need it to recognise image formats.

----------


## FishGuy

referencin system.drawing cleared some more errors but 

```
Dim bmap As Bitmap
            If data.GetDataPresent(GetType(System.Drawing.Bitmap)) Then
                bmap = CType(data.GetData(GetType(System.Drawing.Bitmap)), Bitmap)
                bmap.Save("L:/DeskSpy/" & Now & ".jpg", Imaging.ImageFormat.Jpeg)
            End If
```

Underlined in blue is Bitmap and Imaging _ Name Imaging is not declared and type Bitmap is not defined????????

----------


## FishGuy

Ok I have completed my transformation of it into a windows service - but it doesnt quite work, The service installs and starts but on debugging there is an error. That obeject reference not set to an instance of an object. line 86 (If data.GetDataPresent(GetType(System.Drawing.Bitmap)) Then) any suggestions please. 

```
Option Strict Off
Imports System.ServiceProcess
Imports System.Timers
Imports System.Windows.Forms
Imports System.Drawing


Public Class TimerService
    Inherits System.ServiceProcess.ServiceBase

#Region " Component Designer generated code "

    Public Sub New()
        MyBase.New()

        ' This call is required by the Component Designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call

    End Sub

    'UserService overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    ' The main entry point for the process
    <MTAThread()> _
    Shared Sub Main()
        Dim ServicesToRun() As System.ServiceProcess.ServiceBase

        ' More than one NT Service may run within the same process. To add
        ' another service to this process, change the following line to
        ' create a second service object. For example,
        '
        '   ServicesToRun = New System.ServiceProcess.ServiceBase () {New Service1, New MySecondUserService}
        '
        ServicesToRun = New System.ServiceProcess.ServiceBase() {New TimerService()}

        System.ServiceProcess.ServiceBase.Run(ServicesToRun)
    End Sub

    'Required by the Component Designer
    Private components As System.ComponentModel.IContainer

    ' NOTE: The following procedure is required by the Component Designer
    ' It can be modified using the Component Designer.  
    ' Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        components = New System.ComponentModel.Container()
        Me.ServiceName = "TimerServiceTest"
    End Sub

#End Region

    ' This fires every 10 seconds.
    Private WithEvents ServiceTimer As New System.Timers.Timer(10000)
    Private Counter As Integer

    Protected Overrides Sub OnStart(ByVal args() As String)
        ServiceTimer.Start()
    End Sub

    Protected Overrides Sub OnStop()
        ServiceTimer.Stop()
    End Sub
    Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Integer, ByVal dwExtraInfo As Integer)
    Private Const VK_SNAPSHOT As Short = &H2CS
    Private Sub DoWork(ByVal sender As Object, _
      ByVal e As ElapsedEventArgs) Handles ServiceTimer.Elapsed
        Counter += 1
        Debug.WriteLine("Repetition #" & Counter.ToString())
        Try
            Call keybd_event(System.Windows.Forms.Keys.Snapshot, 0, 0, 0)
            System.Threading.Thread.Sleep(200) ' To have time to catch the clipboard

            Dim data As IDataObject
            data = Clipboard.GetDataObject()
            Dim bmap As Bitmap
            If data.GetDataPresent(GetType(System.Drawing.Bitmap)) Then
                bmap = CType(data.GetData(GetType(System.Drawing.Bitmap)), Bitmap)
                bmap.Save("c:/SpyTest.jpg", Imaging.ImageFormat.Jpeg)
            End If
        Catch Err As Exception
            Debug.WriteLine(Err.ToString())
        End Try
    End Sub

End Class
```

----------


## wossname

FYI, I recently added screen-rectangle grabbing support to BlitterChip.  See here, the attachment might be useful to you....
http://www.vbforums.com/showpost.php...63&postcount=2

----------


## wossname

> That obeject reference not set to an instance of an object. line 86 (If data.GetDataPresent(GetType(System.Drawing.Bitmap)) Then)


Sounds like there was nothing on the clipboard.

----------


## FishGuy

I dont understand why not unless its simply because a service cannot properly invoke Call keybd_event(System.Windows.Forms.Keys.Snapshot, 0, 0, 0)

----------


## FishGuy

Wossname-
I ditched all my old code and used your Blitterchip class, which worked just as i wanted as a windows app but not as a windows service. any idea why? It executes ok and does take a screen grab except the screen grab is just black.
heres my code.
VB Code:
Option Strict On
Imports System.ServiceProcess
Imports System.Timers
Imports System.Drawing
Imports System.Windows.Forms
Public Class TimerService
    Inherits System.ServiceProcess.ServiceBase
 #Region " Component Designer generated code "
     Public Sub New()
        MyBase.New()
         ' This call is required by the Component Designer.
        InitializeComponent()
         ' Add any initialization after the InitializeComponent() call
     End Sub
     'UserService overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub
     ' The main entry point for the process
    <MTAThread()> _
    Shared Sub Main()
        Dim ServicesToRun() As System.ServiceProcess.ServiceBase
         ' More than one NT Service may run within the same process. To add
        ' another service to this process, change the following line to
        ' create a second service object. For example,
        '
        '   ServicesToRun = New System.ServiceProcess.ServiceBase () {New Service1, New MySecondUserService}
        '
        ServicesToRun = New System.ServiceProcess.ServiceBase() {New TimerService}
         System.ServiceProcess.ServiceBase.Run(ServicesToRun)
    End Sub
     'Required by the Component Designer
    Private components As System.ComponentModel.IContainer
     ' NOTE: The following procedure is required by the Component Designer
    ' It can be modified using the Component Designer.  
    ' Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        components = New System.ComponentModel.Container
        Me.ServiceName = "TimerServiceTest"
    End Sub
 #End Region
     ' This fires every 10 seconds.
    Private WithEvents ServiceTimer As New Timers.Timer(10000)
    Private Counter As Integer
     Protected Overrides Sub OnStart(ByVal args() As String)
        ServiceTimer.Start()
    End Sub
     Protected Overrides Sub OnStop()
        ServiceTimer.Stop()
    End Sub
    Private Sub DoWork(ByVal sender As Object, _
   ByVal e As ElapsedEventArgs) Handles ServiceTimer.Elapsed
        Counter += 1
        Debug.WriteLine("Repetition #" & Counter.ToString())
        Try
            Dim i As Bitmap = BlitterChip.ScreenGrab(New Rectangle(0, 0, 1200, 800))
            i.Save("c:/SpyTest1.jpg", Imaging.ImageFormat.Jpeg)
        Catch Err As Exception
            Debug.WriteLine(Err.ToString())
        End Try
    End Sub
 End Class

----------


## MrPolite

with all due respect it's alightly stupid to get a screenshot using print screen. You ruin the guy's clipboard data too  :Big Grin:  I found this a few days before and it's in C#  http://www.developerfusion.com/show/4630/
that gets the screen using APIs, I'm sure some of you gurus can rewrite that in VB :Big Grin:

----------


## wossname

> with all due respect it's alightly stupid to get a screenshot using print screen. You ruin the guy's clipboard data too  I found this a few days before and it's in C#  http://www.developerfusion.com/show/4630/
> that gets the screen using APIs, I'm sure some of you gurus can rewrite that in VB


 Yes it is stupid.  You should read post #16  :Big Grin:   API all the way baby, yeah!

That code on Developerfusion is a bit flabby imho.  Why use 300 api functions when 2 will do?  Blitterchip bungs the image straight into a bitmap for you so you can save it in any format supported by GDI+.

 :Smilie:

----------


## wossname

> Wossname-
> I ditched all my old code and used your Blitterchip class, which worked just as i wanted as a windows app but not as a windows service. any idea why? It executes ok and does take a screen grab except the screen grab is just black.


You code looks OK to me.  I wonder if Services are unable to query video memory?  I have limited Services experience so I don't really know what to do about that, sorry.

----------


## MrPolite

yeah I see it now. All your API are belong to me :Smilie: 
umm one thing though, you are using GetDC(0) to get the desktop dc? is that a legitimate way of doing it? :Big Grin: 
would that always work?

----------


## abhijit

I got a problem with the code posted by wossname. I found that it only captured a corner of the screen. Could someone take a look? Help

----------


## Libero

:Frown:   Thx for hijacking   :Ehh:

----------


## abhijit

Oh, so it was your code then. How do I capture the whole screen?

----------


## Libero

> Oh, so it was your code then. How do I capture the whole screen?


I mean hijacking the thread   :Smilie:  

And as far as i remember, the code i posted capture the whole screen. (havent been programming for a while)  :Sick:

----------


## abhijit

Oh I am sorry about that, but I still can't get this code to work properly.

----------


## conipto

> with all due respect it's alightly stupid to get a screenshot using print screen. You ruin the guy's clipboard data too  I found this a few days before and it's in C#  http://www.developerfusion.com/show/4630/
> that gets the screen using APIs, I'm sure some of you gurus can rewrite that in VB


Can't you just back up the clipboard data then replace it when you're done?

Bill

----------


## MrPolite

> Can't you just back up the clipboard data then replace it when you're done?
> 
> Bill


yeah sure
but when you can get the screenshots using apis, why not just do that instaed  :Smilie:

----------


## conipto

> yeah sure
> but when you can get the screenshots using apis, why not just do that instaed



Well, MS seems to think .NET users should be avoiding API calls.. and seems to be hinting around at their planned obsolesence...

Bill

----------

