The MASM Forum

General => The Workshop => Topic started by: zedd151 on July 20, 2018, 06:14:37 AM

Title: Screen Capture for 32 Bit machines
Post by: zedd151 on July 20, 2018, 06:14:37 AM
I have with his permission, rewritten Vortex's screen capture routine(64 bit) for 32 bit usage. It is not a direct conversion of his program, just
what is necessary to capture the screen and output a bitmap file (in the location of the executable - for now)



This program is just the basics needed to capture the screen by simply clicking the executable. No Console Window, or window of any kind.
Later, I want to add code to let the program sit idly in the system tray until {PrtScr} or some other user definable key combinations are pressed.


Code: [Select]

; (semi)Original idea 'borrowed' from Vortex @ masm32.com
; Originally coded for 64 bit exe
; these are the basic bits and pieces to capture the complete window
; and save to a predetermined .bmp file
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
; Only need to use 'Assemble and Link' from the 'Project' menu, No Console
; Window is neded or desired
;--------------------------------------------------------------------------
;--------------------------------------------------------------------------
; 32 bit small version zedd151 @ masm32.com
;
; Will add code at some point to run the program in the background, awaiting
; the user pressing the 'PrtScr' key (or other key )


include \masm32\include\masm32rt.inc


.data


    hDC         dd 0    ; Device Context handle returned by GetDC
    hBmp        dd 0    ; Bitmap handle returned by CreateCompatibleBitmap
    hOld        dd 0    ; handle returned by SelectObject
    tempDC      dd 0    ; Device Context handle returned by CreateCompatibleDC
    xx          dd 0    ; screen width
    yy          dd 0    ; screen height
    Bits        dd 0    ; bits per pixel x number of color planes
    pMem        dd 0    ; ptr to allocated memory for saving the bitmap image
    hFile       dd 0    ; file handle to created bitmap file
    BmpSize     dd 0    ; size of bitmap data, not including headers
    SizeofDIB   dd 0    ; size of bitmap file, including headers
    BWritten    dd 0    ; pointer used as index for write file


    outfile     db "test.bmp", 0            ; output filename


align 16


    bm          BITMAP              <>      ; BITMAP structure
    BmpFileHdr  BITMAPFILEHEADER    <>      ; BITMAPFILEHEADER structure
    BmpInfoHdr  BITMAPINFOHEADER    <>      ; BITMAPINFOHEADER structure


BITMAP STRUCT
    bmType          DWORD   0   ; The bitmap type. This member must be zero.
    bmWidth         DWORD   0   ; The width, in pixels, of the bitmap. The width must be greater than zero.
    bmHeight        DWORD   0   ; The height, in pixels, of the bitmap. The height must be greater than zero.
    bmWidthBytes    DWORD   0   ; The number of bytes in each scan line. This value must be divisible by 2, ----
                                ; --- because the system assumes that the bit values of a bitmap form an array that is word aligned.
    bmPlanes        WORD    0   ; The count of color planes.
    bmBitsPixel     WORD    0   ; The number of bits required to indicate the color of a pixel.
    bmBits          DWORD   0   ; A pointer to the location of the (byte) values for the bitmap.
BITMAP ENDS


BITMAPFILEHEADER STRUCT
    bfType          WORD    0   ; The file type; must be BM.
    bfSize          DWORD   0   ; The size, in bytes, of the bitmap file.
    bfReserved1     WORD    0   ; Reserved; must be zero.
    bfReserved2     WORD    0   ; Reserved; must be zero.
    bfOffBits       DWORD   0   ; The offset, in bytes, from the beginning of the BITMAPFILEHEADER structure to the bitmap bits.
BITMAPFILEHEADER ENDS




BITMAPINFOHEADER STRUCT
    biSize          DWORD   0   ; Specifies the number of bytes required by the structure.
    biWidth         DWORD   0   ; Specifies the width of the bitmap, in pixels.
    biHeight        DWORD   0   ; Specifies the height of the bitmap, in pixels.
    biPlanes        WORD    0   ; Specifies the number of planes for the target device. This value must be set to 1.
    biBitCount      WORD    0   ; Specifies the number of bits per pixel (bpp).
    biCompression   DWORD   0   ; use BI_RGB for uncompressed RGB Bitmaps
    biSizeImage     DWORD   0   ; set to 0 for uncompressed RGB bitmaps.
    biXPelsPerMeter DWORD   0   ; set to 0
    biYPelsPerMeter DWORD   0   ; set to 0
    biClrUsed       DWORD   0   ; set to 0
    biClrImportant  DWORD   0   ; Specifies the number of color indices that are considered important for displaying the bitmap. ---
                                ; ----   If this value is zero, all colors are important.
BITMAPINFOHEADER ENDS




.code




start:


    lea     edi, bm


    invoke  GetDC, 0
    mov     hDC, eax
   
    invoke  CreateCompatibleDC, hDC
    mov     tempDC, eax


    invoke  GetSystemMetrics, SM_CXSCREEN
    mov     xx, eax
   
    invoke  GetSystemMetrics, SM_CYSCREEN
    mov     yy, eax
   
    invoke  CreateCompatibleBitmap, hDC, xx, yy
    mov     hBmp, eax
   
    invoke  SelectObject, tempDC, hBmp
    mov     hOld, eax


    invoke  BitBlt, tempDC, 0, 0, xx, yy, hDC, 0, 0, SRCCOPY






    invoke  GetObject, hBmp, sizeof(BITMAP), edi


    mov     BITMAP.bmBitsPixel[edi], 24


    xor     edx, edx
    movzx   eax, BITMAP.bmPlanes[edi]
    mul     BITMAP.bmBitsPixel[edi]
    mov     Bits, eax


    lea     ecx, BmpInfoHdr


    mov     BITMAPINFOHEADER.biSize[ecx], sizeof(BITMAPINFOHEADER)


    mov     eax, xx
    mov     BITMAPINFOHEADER.biWidth[ecx],eax


    mov     eax, yy
    mov     BITMAPINFOHEADER.biHeight[ecx],eax
   
    mov     ax,BITMAP.bmPlanes[edi]
    mov     BITMAPINFOHEADER.biPlanes[ecx],ax


    mov     ax,BITMAP.bmBitsPixel[edi]
    mov     BITMAPINFOHEADER.biBitCount[ecx],ax


    mov     BITMAPINFOHEADER.biCompression[ecx], BI_RGB
    xor     eax, eax
    mov     BITMAPINFOHEADER.biSizeImage[ecx], eax
    mov     BITMAPINFOHEADER.biXPelsPerMeter[ecx], eax
    mov     BITMAPINFOHEADER.biYPelsPerMeter[ecx], eax 
    mov     BITMAPINFOHEADER.biClrUsed[ecx], eax
    mov     BITMAPINFOHEADER.biClrImportant[ecx], eax


    xor     edx, edx
    mov     eax, BITMAP.bmWidth[ecx]


 
    mul     Bits
    add     eax, 31
    and     eax, -32
    shr     eax, 3
    mul     BITMAP.bmHeight[ecx]
    mov     BmpSize, eax


    invoke  VirtualAlloc, 0, eax, MEM_COMMIT, PAGE_READWRITE


    mov     pMem, eax


    invoke  GetDIBits, tempDC, hBmp, 0, dword PTR BITMAP.bmHeight[edi], pMem, ADDR BmpInfoHdr, DIB_RGB_COLORS


    xor     eax,eax
    invoke  CreateFile, addr outfile, GENERIC_WRITE, eax, eax, CREATE_ALWAYS, eax, eax


    mov     hFile, eax


    mov     eax, BmpSize
   
    add     eax, sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)


    mov     SizeofDIB, eax


    lea     edx, BmpFileHdr
    mov     BITMAPFILEHEADER.bfOffBits[edx], sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)


    mov     eax, SizeofDIB
    mov     BITMAPFILEHEADER.bfSize[edx], eax


    mov     BITMAPFILEHEADER.bfReserved1[edx], 0
    mov     BITMAPFILEHEADER.bfReserved2[edx], 0


    mov     BITMAPFILEHEADER.bfType[edx], 'MB'


    lea     esi, BWritten
   
    invoke  WriteFile, hFile, ADDR BmpFileHdr, sizeof(BITMAPFILEHEADER), esi, 0
           
    invoke  WriteFile,hFile, ADDR BmpInfoHdr, sizeof(BITMAPINFOHEADER), esi, 0
           
    invoke  WriteFile, hFile, pMem, BmpSize, esi, 0


    invoke  CloseHandle, hFile


    invoke  VirtualFree, pMem, 0, MEM_RELEASE   
     
    invoke  SelectObject, tempDC, hOld
    invoke  DeleteObject, hBmp
    invoke  DeleteDC, tempDC
    invoke  ReleaseDC, 0, hDC


    invoke  ExitProcess, 0
end start




If you do make some screen captures and want to post them, please use an external hosting site. hutch-- has indicated his concern over disk space usage and too many image uploads to the site. I use DropBox myself.  When linking an image or other link from DropBox, the link ends with "dl=0" you have to manually change to "dl=1" for the direct link to work properly.   8)



---------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------
Instructions for use on Windows xp. The way I use it (as-is 'out-of-the-box). 


Place the exe in a convenient location. create a shortcut in 'quick launch' (on the taskbar) and voila.
Simply single click the icon for the screen capture tool that is in quick launch.

Wherever you placed the exe, is where the screen capture "test.bmp" will be created.


Fully tested for use in Windows XP 32 bit, and Windows 7 32 bit. Have not tested in other platforms.

Vortex's 64 bit Screen Capture tool can be found  HERE (http://masm32.com/board/index.php?topic=7185.msg77638#msg77638)


32 bit version attached below
Title: Re: Screen Capture for 32 Bit machines
Post by: Vortex on July 20, 2018, 06:23:48 AM
Hi zedd,

Thanks for your work. I will test it.
Title: Re: Screen Capture for 32 Bit machines
Post by: aw27 on July 20, 2018, 04:58:02 PM
Nice to see you back to coding.
On this, I would like to capture what's on my 2nd monitor.  :(
Title: Re: Screen Capture for 32 Bit machines
Post by: zedd151 on July 21, 2018, 11:03:57 AM
Nice to see you back to coding.


Yeah, it's good to be back.


Quote
On this, I would like to capture what's on my 2nd monitor.  :(


I'm just the guy supplying a 32 bit garden variety basic tool for screen capture.   :biggrin:


Your second monitor is connected to a computer, isn't it?   :P
Just kidding. If it is some sort of extended desktop it is probably possible. But I haven't a clue how to do that.


Should be possible to retrieve the handle, then go from there.
Title: Re: Screen Capture for 32 Bit machines
Post by: zedd151 on July 21, 2018, 11:21:31 AM
"Small code" version of the 32 bit Screen Capture tool.


Code: [Select]

include \masm32\include\masm32rt.inc


.data


    hDC         dd  0
    hBmp        dd  0
    hOld        dd  0
    tempDC      dd  0
    Bits        dd  24
    pMem        dd  0
    hFile       dd  0
    BmpSize     dd  0
    SizeofDIB   dd  0
    BWritten    dd  0
   
    ;;;;;;;;;;;;;;;;;;;;; pseudo BITMAP HEADERS ;;;;;;;;;;;;;;;;;;;;;;;
    ;  used similar names, and predefined the data for certain elements
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   
    zbfType     db  "BM"
    zbfSize     dd  0
                dw  0
                dw  0
                dd  54
    zbiSize     dd  40
    zbiWidth    dd  0
    zbiHeight   dd  0
                dw  1
                dw  24
                dd  0
                dd  0
                dd  0
                dd  0
                dd  0
                dd  0
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    outfile     db "test.bmp", 0
.code
start:
    xor ebx, ebx
    invoke  GetDC, ebx
    mov     hDC, eax
    invoke  CreateCompatibleDC, hDC
    mov     tempDC, eax
    invoke  GetSystemMetrics, ebx
    mov     zbiWidth, eax
    invoke  GetSystemMetrics, SM_CYSCREEN
    mov     zbiHeight, eax
    invoke  CreateCompatibleBitmap, hDC, zbiWidth, zbiHeight
    mov     hBmp, eax
    invoke  SelectObject, tempDC, hBmp
    mov     hOld, eax
    invoke  BitBlt, tempDC, ebx, ebx, zbiWidth, zbiHeight, hDC, ebx, ebx, SRCCOPY
    invoke  GetObject, hBmp, 24, edi
    xor     edx, edx
    mov     eax, zbiWidth
    mul     Bits
    add     eax, 31
    and     eax, -32
    shr     eax, 3
    mul     zbiHeight
    mov     BmpSize, eax
    invoke  VirtualAlloc, ebx, eax, MEM_COMMIT, PAGE_READWRITE
    mov     pMem, eax
    invoke  GetDIBits, tempDC, hBmp, ebx, zbiHeight, pMem, ADDR zbiSize, ebx
    invoke  CreateFile, addr outfile, GENERIC_WRITE, ebx, ebx, CREATE_ALWAYS, ebx, ebx
    mov     hFile, eax
    mov     eax, BmpSize
    add     eax, 54
    mov     SizeofDIB, eax
    mov     eax, SizeofDIB
    mov     zbfSize, eax
    lea     esi, BWritten
    invoke  WriteFile, hFile, ADDR zbfType, 54, esi, ebx
    invoke  WriteFile, hFile, pMem, BmpSize, esi, ebx
    invoke  CloseHandle, hFile
    invoke  VirtualFree, pMem, ebx, MEM_RELEASE         
    invoke  SelectObject, tempDC, hOld
    invoke  DeleteObject, hBmp
    invoke  DeleteDC, tempDC
    invoke  ReleaseDC, ebx, hDC
    invoke  ExitProcess, ebx
end start


I noticed ebx never changed during the enitire programs run, so am using ebx in place of zero
saves bytes all over the place. Also certain elements are initialized as we aren't expecting to change those values.


I.e., 24 bit color, and 1 color plane. For the pseudo headers only the values that will change during run time
have names - plus the variables that are referenced. (start of BITMAPFILEHEADER is the address of zbfType, etc)   :biggrin:


All the structure elements that previously were initialised with "mov (element name), 0" or similar, the mov instructions
have been removed as all the elements are pre-initialised.  :biggrin:


Bugs were fully tested and are working properly.  :P


Tested in Windows XP 32bit, and Windows 7 32 bit.


May not work on all flavors and sizes.   :P  See the first post for the original version. 
Title: Re: Screen Capture for 32 Bit machines
Post by: hutch-- on July 21, 2018, 11:43:10 AM
Z,

Be careful about using EBX, it is a non-volatile register and it will come back to bite you in unexpected places. Execution speed is barely related to code size where instruction choice and count are the things that matter. A 32 bit processor will munch an immediate close enough to as fast as a register so you are not losing much to use "0" instead of EBX.
Title: Re: Screen Capture for 32 Bit machines
Post by: zedd151 on July 21, 2018, 11:48:12 AM
Z,

Be careful about using EBX, it is a non-volatile register and it will come back to bite you in unexpected places.


Yes, I know.  But I have checked it in both Ollydbg and x32dbg, and both show that ebx remains unchanged during the entire very short
run of the program. This was just an exercise in reducing the code.  I have checked it both in ollydbg and x32dbg and it runs just fine.
Title: Re: Screen Capture for 32 Bit machines
Post by: felipe on July 21, 2018, 01:06:14 PM
If you are not using ebx in a callback procedure then is perfectly right to use it all along your code. The api functions used in your program will save it and restore it in entry and the exit of execution.  :idea:
Title: Re: Screen Capture for 32 Bit machines
Post by: zedd151 on July 21, 2018, 01:08:28 PM
If you are not using ebx in a callback procedure then is perfectly right to use it all along your code. The api functions used in your program will save it and restore it in entry and the exit of execution.  :idea:


Thats exactly right, as I had observed in the debuggers. So no problems there.
Title: Re: Screen Capture for 32 Bit machines
Post by: hutch-- on July 21, 2018, 01:44:36 PM
I always laugh at folks who like to live dangerously, for years I have heard "It runs OK on [Put your Windows version here]) but another Windows version will bite you on the ass when you run it. You have to remember when Microsoft rewrite (and often stuff up) an API for a new version, they generally try and ensure that it complies with the Intel ABI (for Win32) so even if something worked OK on one version, there is a risk that it won't run an another if it is not fully ABI compliant.

If you need an extra register, do the normal preserve with it and you are safe on any version. If you write a proc which calls other procs but does not call any external procedures that are general purpose, you can preserve all of the registers you want to use then in a closed nesting of procedures you can do anything you like. It like having a private call tree that nothing else an use and it is accessed only from one point.
Title: Re: Screen Capture for 32 Bit machines
Post by: zedd151 on July 21, 2018, 02:08:04 PM
@hutch


Points well taken.  :t The version in the first post is the "official" version.
Title: Re: Screen Capture for 32 Bit machines
Post by: aw27 on July 21, 2018, 03:09:44 PM
Your second monitor is connected to a computer, isn't it?   :P
Just kidding. If it is some sort of extended desktop it is probably possible. But I haven't a clue how to do that.
Not extended desktop, the desktop is made up of all the screens. I still see software that when launched is positioned between 2 screens - because the author has centered on the desktop.
Anyway, don't bother it was just a challenge.
Title: Re: Screen Capture for 32 Bit machines
Post by: felipe on July 22, 2018, 07:25:52 AM
You have to remember when Microsoft rewrite (and often stuff up) an API for a new version, they generally try and ensure that it complies with the Intel ABI (for Win32) so even if something worked OK on one version, there is a risk that it won't run an another if it is not fully ABI compliant.

If the abi for win32 says that the called function should preserve the non volatile registers, then using ebx in a non callback function in your own code is abi compliant.  :idea:
Title: Re: Screen Capture for 32 Bit machines
Post by: 2B||!2B on July 22, 2018, 02:11:54 PM
Hi zedd,

Excellent effort.  :greenclp:

Does your code work on a 64bit machine?
It works, tested on Win 7 64bit :)

Is there anyway you can make a quality selection? maybe just a hardcoded value to set in the project for the quality of the image? or the type? maybe PNG for smaller output file.

Is it possible to add a show cursor feature?

Thanks.
Title: Re: Screen Capture for 32 Bit machines
Post by: zedd151 on July 22, 2018, 02:20:35 PM
Does your code work on a 64bit machine?


I haven't even tested on 64 bit OS, as Vortex has a 64 bit Screen Capture tool  HERE  (http://masm32.com/board/index.php?topic=7185.msg77638#msg77638) and I am not competing with his tool.


Quote
Is there anyway you can make a quality selection? maybe just a hardcoded value to set in the project for the quality of the image.


As it stands right now, it captures the full window with 24 bit color depth. 


If you wish to modify the code for your own purposes, feel free to do so. I plan on developing this further but not at this time, as I have another project that I am working on.
Title: Re: Screen Capture for 32 Bit machines
Post by: 2B||!2B on July 22, 2018, 02:32:12 PM
Does your code work on a 64bit machine?


I haven't even tested on 64 bit OS, as Vortex has a 64 bit Screen Capture tool  HERE  (http://masm32.com/board/index.php?topic=7185.msg77638#msg77638) and I am not competing with his tool.


Quote
Is there anyway you can make a quality selection? maybe just a hardcoded value to set in the project for the quality of the image.


As it stands right now, it captures the full window with 24 bit color depth. 


If you wish to modify the code for your own purposes, feel free to do so. I plan on developing this further but not at this time, as I have another project that I am working on.

Cool man thanks. I tested it on a 64bit system and it worked.
Is there any difference between this method of screen capturing and the GDI+ Api's?
Title: Re: Screen Capture for 32 Bit machines
Post by: zedd151 on July 22, 2018, 02:40:02 PM
Is there any difference between this method of screen capturing and the GDI+ Api's?


sure, less code.   :P  Actually I haven't a clue about other screen capture tools gdi plus or otherwise.


I needed a screen cap tool for my switch to Windows XP 32 bit thats why I did this.


as far as the api's used:


Code: [Select]

gdi32.BitBlt
gdi32.CreateCompatibleBitmap
gdi32.CreateCompatibleDC
gdi32.DeleteDC
gdi32.DeleteObject
gdi32.GetDIBits
gdi32.GetObjectA
gdi32.SelectObject
USER32.GetDC
USER32.GetSystemMetrics
USER32.ReleaseDC
kernel32.CloseHandle
kernel32.CreateFileA
kernel32.ExitProcess
kernel32.VirtualAlloc
kernel32.VirtualFree
kernel32.WriteFile
Title: Re: Screen Capture for 32 Bit machines
Post by: daydreamer on July 22, 2018, 05:14:32 PM
Z,it would be nice to have a screencapture tool,that capture to video that works together with xp and ddraw, so it's possible to put up old stuff on YouTube, old stuff that won't work on newer windows
Title: Re: Screen Capture for 32 Bit machines
Post by: zedd151 on July 22, 2018, 06:02:12 PM
Z,it would be nice to have a screencapture tool,that capture to video...

I'm sure that would be nice, but unfortunately time is a huge factor. Not enough time to do everything.
 
I made this little tool for a specific purpose for myself, and posted it for anyone else that might find it useful.
 
Anyone can use the sourcecode and modify it to their hearts content, but at present I don't have a lot of time
to do much more with it as I am working on another project which is taking up most of my leisure (coding) time.
Title: Re: Screen Capture for 32 bit - revisited…
Post by: zedd151 on December 10, 2022, 03:46:06 AM
Has anyone done coding to convert bitmap images to .jpg or .png? Would probably require using GDI+ if I am not mistaken. Another option is to natively capture the screen directly to .jpg or .png. Any advice? If necessary I could *attempt* to code it in 64 bit…
Title: Re: Screen Capture for 32 Bit machines
Post by: Vortex on December 10, 2022, 07:44:37 AM
Hi zedd,

http://masm32.com/board/index.php?topic=8483.0

http://masm32.com/board/index.php?topic=8495.0

http://masm32.com/board/index.php?topic=7514.0

http://masm32.com/board/index.php?topic=4731.0
Title: Re: Screen Capture for 32 Bit machines
Post by: zedd151 on December 10, 2022, 07:49:33 AM
lol - thanks Vortex, I haven't done a proper forum search apparently  :undecided: .  I will look into those later this evening. Should be a time saver, not having to convert bmp to jpg for posting.  :biggrin:
Title: Re: Screen Capture for 32 Bit machines
Post by: daydreamer on December 10, 2022, 08:28:02 PM
lol - thanks Vortex, I haven't done a proper forum search apparently  :undecided: .  I will look into those later this evening. Should be a time saver, not having to convert bmp to jpg for posting.  :biggrin:
When postwork image,bmp or png lossless compression preferred, to export final image much compressed, so it would be nice to have user setting for image format
I gonna try make a bmp rle compressor, it was tedious to type in rle encoded map manually in. Data section, but good enough for testing purposes
Title: Re: Screen Capture for 32 Bit machines
Post by: zedd151 on December 12, 2022, 12:17:37 AM
I gonna try make a bmp rle compressor...
That sounds like it would be above my pay grade.  :tongue: