The MASM Forum
General => The Workshop => Topic started 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.
; (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
-
Hi zedd,
Thanks for your work. I will test it.
-
Nice to see you back to coding.
On this, I would like to capture what's on my 2nd monitor. :(
-
Nice to see you back to coding.
Yeah, it's good to be back.
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.
-
"Small code" version of the 32 bit Screen Capture tool.
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.
-
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.
-
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.
-
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:
-
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.
-
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.
-
@hutch
Points well taken. :t The version in the first post is the "official" version.
-
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.
-
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:
-
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.
-
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.
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.
-
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.
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?
-
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:
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
-
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
-
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.
-
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…
-
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
-
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:
-
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
-
I gonna try make a bmp rle compressor...
That sounds like it would be above my pay grade. :tongue: