Virtual Developer Workshop: Containerized Development with Docker
Welcome to my article. Today's topic is quite a frequent topic on the forums. Everyone, especially newbies, wants to know this; hence, this article.
The Windows API
Before explaining how to make a screen capture, I need to speak about the background technology used to copy the screen's content into a file compatible with a drawing program. This technology is known as the Winodws API.
Now, What Is the Windows API?
A simple action, such as opening a file, executes the desired functionality needed for the file to be run. Copying and pasting is also an example.
Then, you get the functions that run in the background and you usually do not have to care about: keeping track of memory usage, obtaining a list of running programs. I can go on.
Where Are These APIs Stored?
The Windows system, or any system for that matter, is made of a bunch of files, called DLLs amongst others. The term DLL means Dynamic Link Library. These files are dynamically linked with the Operating System to run the associated program with it. The methods of the programs are locked away in these files, and these methods make your applications work.
Now, to get back on topic. We need to make use of the specific methods inside the specific Windows DLL files to create a screen capture.
Create a new Visual Basic Windows Forms project and name it Screen Capture.
Set the Form's WindowState property to Maximized. This will ensure the form is shown full screen.
Add a PictureBox to your Form. You may name it anything you like, but keep in mind that I have kept the default names.
Set the PictureBox's Dock Property to Fill. This ensures the Picturebox fills up the entire form.
Add the following code just below your Class declaration:
Private Declare Function CreateDC Lib "gdi32" Alias _ "CreateDCA" (ByVal lpDriverName As String, _ ByVal lpDeviceName As String, ByVal lpOutput As String, _ ByVal lpInitData As String) As Integer Private Declare Function CreateCompatibleDC Lib "GDI32" _ (ByVal hDC As Integer) As Integer Private Declare Function CreateCompatibleBitmap Lib "GDI32" _ (ByVal hDC As Integer, ByVal nWidth As Integer, _ ByVal nHeight As Integer) As Integer Private Declare Function GetDeviceCaps Lib "gdi32" Alias _ "GetDeviceCaps" (ByVal hdc As Integer, _ ByVal nIndex As Integer) As Integer Private Declare Function SelectObject Lib "GDI32" _ (ByVal hDC As Integer, ByVal hObject As Integer) As Integer Private Declare Function BitBlt Lib "GDI32" _ (ByVal srchDC As Integer, _ ByVal srcX As Integer, ByVal srcY As Integer, _ ByVal srcW As Integer, ByVal srcH As Integer, _ ByVal desthDC As Integer, ByVal destX As Integer, _ ByVal destY As Integer, ByVal op As Integer) As Integer Private Declare Function DeleteDC Lib "GDI32" _ (ByVal hDC As Integer) As Integer Private Declare Function DeleteObject Lib "GDI32" _ (ByVal hObj As Integer) As Integer
These functions are Windows functions inside a DLL file. Here is a small breakdown of how this code works:
- Private is the Scope which you need, as in Public, Private, or local.
- Declare notifies Visual Basic that we will be using an external function inside our program.
- Function: I do not need to tell you the difference between a Function and a Sub, do I? Okay, a function returns a value, whereas a Sub does not. In this case, we will be dealing a returned value.
- CreateDC (for example) is the name of the Sub or Function we want to use.
- Lib indicates what comes next is the DLL file to look for.
- Gdi32 (for example) basically means gdi32.dll. So, now we have the file and the function inside the file we want, what else is there?
- The remaining part of each line is the parameters for the function or sub, and then the function's return value.
For a list of Windows APIs, have a look here.
Add the following Variables and Constants we will use in our program:
Const SRCCOPY As Integer = &HCC0020 Private bmpBackground As Bitmap Private intWidth, intHeight As Integer
SRCCOPY is a Windows Constant that will be used with these APIs.
Now the fun starts!
Add the following sub to your program:
Protected Sub CaptureScreen() Dim hsdc, hmdc As Integer Dim bmpHandle, OLDbmpHandle As Integer Dim releaseDC As Integer hsdc = CreateDC("DISPLAY", "", "", "") hmdc = CreateCompatibleDC(hsdc) intWidth = GetDeviceCaps(hsdc, 8) intHeight = GetDeviceCaps(hsdc, 10) bmpHandle = CreateCompatibleBitmap(hsdc, _ intWidth, intHeight) OLDbmpHandle = SelectObject(hmdc, bmpHandle) releaseDC = BitBlt(hmdc, 0, 0, intWidth, _ intHeight, hsdc, 0, 0, 13369376) bmpHandle = SelectObject(hmdc, OLDbmpHandle) releaseDC = DeleteDC(hsdc) releaseDC = DeleteDC(hmdc) bmpBackground = _ Image.FromHbitmap(New IntPtr(bmpHandle)) DeleteObject(bmpHandle) End Sub
- This sub looks more complicated as it is. The variables are created that will be used within the sub. Then, a compatible Device Context is created via the CreateDC and the CreateCompatibleDC APIs.
- GetDeviceCaps sets or gets the bits per pixel values for the screen resolution.
- CreateCompatibleBitmap creates the height and width for the Bitmap (picture) we'd like to save.
- The picture physically gets copied via the BitBlt API function.
- We then delete all the memory references used with DeleteDC and DeleteObject.
Not too complicated, hey? Nope.
Add the last part of the code:
Private Sub PictureBox1_Click(sender As Object, _ e As EventArgs) Handles PictureBox1.Click CaptureScreen() PictureBox1.Image = bmpBackground PictureBox1.Image.Save("Capture.jpg") End Sub
When clicked upon, the PictureBox will produce a copy of the screen.
Okay, this is just a simple example, but you can play around with it. I am including a code sample with this article.
I hope you enjoyed today's article. Until next time, cheers!