I’ve tried to code a Windows screenshot utility before, following the example of the official Windows documentation I found it tricky though, so I just gave up at the time.
This Monday morning, after receiving a request to implement GetDIBits in WinSafe, I tried to implement it Windigo first. To my surprise, it went incredibly smooth. Go’s defer mechanism is much to praise.
Here’s the whole code
package main
import (
"runtime"
"unsafe"
"github.com/rodrigocfd/windigo/win"
"github.com/rodrigocfd/windigo/win/co"
)
func main() {
runtime.LockOSThread()
cxScreen := win.GetSystemMetrics(co.SM_CXSCREEN)
cyScreen := win.GetSystemMetrics(co.SM_CYSCREEN)
hdcScreen := win.HWND(0).GetDC()
defer win.HWND(0).ReleaseDC(hdcScreen)
hBmp := hdcScreen.CreateCompatibleBitmap(cxScreen, cyScreen)
defer hBmp.DeleteObject()
hdcMem := hdcScreen.CreateCompatibleDC()
defer hdcMem.DeleteDC()
hBmpOld := hdcMem.SelectObjectBitmap(hBmp)
defer hdcMem.SelectObjectBitmap(hBmpOld)
hdcMem.BitBlt(
win.POINT{X: 0, Y: 0},
win.SIZE{Cx: cxScreen, Cy: cyScreen},
hdcScreen,
win.POINT{X: 0, Y: 0},
co.ROP_SRCCOPY,
)
bi := win.BITMAPINFO{
BmiHeader: win.BITMAPINFOHEADER{
BiWidth: cxScreen,
BiHeight: cyScreen,
BiPlanes: 1,
BiBitCount: 32,
BiCompression: co.BI_RGB,
},
}
bi.BmiHeader.SetBiSize()
bmpObj := win.BITMAP{}
hBmp.GetObject(&bmpObj)
bmpSize := bmpObj.CalcBitmapSize(bi.BmiHeader.BiBitCount)
rawMem := win.GlobalAlloc(co.GMEM_FIXED|co.GMEM_ZEROINIT, bmpSize)
defer rawMem.GlobalFree()
bmpSlice := rawMem.GlobalLock(bmpSize)
defer rawMem.GlobalUnlock()
hdcScreen.GetDIBits(hBmp, 0, int(cyScreen), bmpSlice, &bi, co.DIB_RGB_COLORS)
bfh := win.BITMAPFILEHEADER{}
bfh.SetBfType()
bfh.SetBfOffBits(uint32(unsafe.Sizeof(bfh) + unsafe.Sizeof(bi.BmiHeader)))
bfh.SetBfSize(bfh.BfOffBits() + uint32(bmpSize))
fo, _ := win.FileOpen("C:\\Temp\\foo.bmp", co.FILE_OPEN_RW_OPEN_OR_CREATE)
defer fo.Close()
fo.Write(bfh.Serialize())
fo.Write(bi.BmiHeader.Serialize())
fo.Write(bmpSlice)
println("Done")
}
For reference, this example is now on GetDIBits documentation.