Skip to content

Latest commit

 

History

History
290 lines (230 loc) · 8.86 KB

sample_189.md

File metadata and controls

290 lines (230 loc) · 8.86 KB

Home

Storing content of the Clipboard to a bitmap file

Before you begin:

To test this code copy something "bmp-like" to the Windows clipboard. Cut or copy a picture fragment inside a graphics application or just press the Print Screen key.

Make sure the destination path and file name are valid.

See also:


Code:

#DEFINE CF_BITMAP 2
#DEFINE OBJ_BITMAP 7
#DEFINE DIB_RGB_COLORS 0
#DEFINE GENERIC_WRITE 0x40000000
#DEFINE FILE_SHARE_WRITE 2
#DEFINE CREATE_ALWAYS 2
#DEFINE FILE_ATTRIBUTE_NORMAL 128
#DEFINE INVALID_HANDLE_VALUE -1
#DEFINE BI_RGB 0
#DEFINE GMEM_FIXED 0
#DEFINE BFHDR_SIZE 14  && BITMAPFILEHEADER
#DEFINE BHDR_SIZE 40  && BITMAPINFOHEADER
#DEFINE RGBQUAD_SIZE 4  && RGBQUAD
#DEFINE BHDR_SIZE 40  && BITMAPINFOHEADER
#DEFINE BITMAP_STRU_SIZE 24

DO declare

LOCAL hClipBmp, lcTargetFile
lcTargetFile = "c:\temp\clipboard.bmp"

= OpenClipboard(0)
hClipBmp = GetClipboardData(CF_BITMAP)
= CloseClipboard()

IF hClipBmp = 0 Or GetObjectType(hClipBmp) <> OBJ_BITMAP
	= MESSAGEBOX("No bitmap data found on the clipboard.     " +;
		CHR(13)+CHR(13)+;
		"Press Print Screen key and test again.     ",;
		64, "Clipboard to BMP")
	RETURN
ENDIF

= bitmap2file(hClipBmp, lcTargetFile)
= DeleteObject(hClipBmp)
* end of main

PROCEDURE bitmap2file(hBitmap, lcTargetFile)
	PRIVATE pnWidth, pnHeight, pnBitsSize, pnRgbQuadSize, pnBytesPerScan
	STORE 0 TO pnWidth, pnHeight, pnBytesPerScan, pnBitsSize, pnRgbQuadSize
	= GetBitmapDimensions(hBitmap, @pnWidth, @pnHeight)

	LOCAL lpBitsArray, lcBInfo

	lcBInfo = InitBitmapInfo()
	lpBitsArray = InitBitsArray()

	LOCAL hwnd, hdc, hMemDC
	hwnd = GetActiveWindow()
	hdc = GetWindowDC(hwnd)
	hMemDC = CreateCompatibleDC(hdc)
	= ReleaseDC(hwnd, hdc)

	= GetDIBits(hMemDC, hBitmap, 0, pnHeight, lpBitsArray,;
			@lcBInfo, DIB_RGB_COLORS)

	LOCAL hFile, lnFileSize, lnOffBits, lcBFileHdr

	* bitmap file size
	lnFileSize = BFHDR_SIZE + BHDR_SIZE + pnRgbQuadSize + pnBitsSize

	* offset to the bitmap bits
	lnOffBits = BFHDR_SIZE + BHDR_SIZE + pnRgbQuadSize
	
	* bitmap file header
	lcBFileHdr = "BM" + num2dword(lnFileSize) +;
			num2dword(0) + num2dword(lnOffBits)

	* target file handle
	hFile = CreateFile(lcTargetFile,;
				GENERIC_WRITE,;
				FILE_SHARE_WRITE, 0,;
				CREATE_ALWAYS,;
				FILE_ATTRIBUTE_NORMAL, 0)

	IF hFile <> INVALID_HANDLE_VALUE
	* a straightforward process of storing block after block:
		WAIT WINDOW "Storing to file..." NOWAIT

		= String2File(hFile, @lcBFileHdr)           && BitmapFileHeader
		= String2File(hFile, @lcBInfo)              && BitmapInfo
		= Ptr2File(hFile, lpBitsArray, pnBitsSize)  && bitmap data
		= CloseHandle(hFile)
		
		WAIT CLEAR
		IF MESSAGEBOX("File created " +;
			lcTargetFile + Chr(13)+Chr(13) +;
			LTRIM(STR(lnFileSize)) + " bytes" + Chr(13) +;
			LTRIM(STR(pnWidth)) + " x " +;
			LTRIM(STR(pnHeight)) + Chr(13) + CHR(13) +;
			"Press YES to open this file " +;
			"in the default application.     ",;
			32+4, "Clipboard to BMP") = 6

			= ShellExecute(0, "open", m.lcTargetFile,;
				"", "", 3)
		ENDIF
	ELSE
		= MESSAGEBOX("Unable to create file " +;
			lcTargetFile + "     ", 48, "Error")
	ENDIF

	= GlobalFree(lpBitsArray)
	= DeleteDC(hMemDC)
RETURN

PROCEDURE GetBitmapDimensions(hBitmap, lnWidth, lnHeight)
	LOCAL lcBuffer
	lcBuffer = Repli(Chr(0), BITMAP_STRU_SIZE)
	IF GetObjectA(hBitmap, BITMAP_STRU_SIZE, @lcBuffer) <> 0
		lnWidth  = buf2dword(SUBSTR(lcBuffer, 5,4))
		lnHeight = buf2dword(SUBSTR(lcBuffer, 9,4))
	ENDIF
RETURN

PROCEDURE InitBitmapInfo(lcBIHdr)

	LOCAL lnBitsPerPixel, lcBIHdr, lcRgbQuad

	* forcing 24-bit format
	lnBitsPerPixel = 24
	pnBytesPerScan = Int((pnWidth * lnBitsPerPixel)/8)

	* line width should be DWORD-aligned (4 bytes)
	* important for 16- and 24-bit images
	IF Mod(pnBytesPerScan, 4) <> 0
		pnBytesPerScan = pnBytesPerScan + 4 - Mod(pnBytesPerScan, 4)
	ENDIF

	* initializing BitmapInfoHeader structure
	lcBIHdr = num2dword(BHDR_SIZE) + num2dword(pnWidth) +;
		num2dword(pnHeight) + num2word(1) + num2word(lnBitsPerPixel) +;
		num2dword(BI_RGB) + Repli(Chr(0), 20)

	* creating a buffer for the color table
	IF lnBitsPerPixel <= 8
		pnRgbQuadSize = (2^lnBitsPerPixel) * RGBQUAD_SIZE
		lcRgbQuad = Repli(Chr(0), pnRgbQuadSize)
	ELSE
		lcRgbQuad = ""
	ENDIF
RETURN  lcBIHdr + lcRgbQuad

PROCEDURE InitBitsArray()
	LOCAL lnPtr

	pnBitsSize = pnHeight * pnBytesPerScan
	lnPtr = GlobalAlloc(GMEM_FIXED, pnBitsSize)
	= ZeroMemory(lnPtr, pnBitsSize)
RETURN  lnPtr

PROCEDURE String2File(hFile, lcBuffer)
* appends string buffer to a file
	DECLARE INTEGER WriteFile IN kernel32;
		INTEGER hFile, STRING @lpBuffer, INTEGER nBt2Write,;
		INTEGER @lpBtWritten, INTEGER lpOverlapped

	= WriteFile(hFile, @lcBuffer, Len(lcBuffer), 0, 0)
RETURN

PROCEDURE Ptr2File(hFile, lnPointer, lnBt2Write)
* appends memory block to a file
	DECLARE INTEGER WriteFile IN kernel32;
		INTEGER hFile, INTEGER lpBuffer, INTEGER nBt2Write,;
		INTEGER @lpBtWritten, INTEGER lpOverlapped

	= WriteFile(hFile, lnPointer, lnBt2Write, 0, 0)
RETURN

FUNCTION buf2dword(cBuffer)
RETURN Asc(SUBSTR(cBuffer, 1,1)) + ;
	BitLShift(Asc(SUBSTR(cBuffer, 2,1)),  8) +;
	BitLShift(Asc(SUBSTR(cBuffer, 3,1)), 16) +;
	BitLShift(Asc(SUBSTR(cBuffer, 4,1)), 24)

FUNCTION buf2word(lcBuffer)
RETURN Asc(SUBSTR(lcBuffer, 1,1)) + ;
       Asc(SUBSTR(lcBuffer, 2,1)) * 256

FUNCTION num2dword(lnValue)
#DEFINE m0 0x0000100
#DEFINE m1 0x0010000
#DEFINE m2 0x1000000
	IF lnValue < 0
		lnValue = 0x100000000 + lnValue
	ENDIF
	LOCAL b0, b1, b2, b3
	b3 = Int(lnValue/m2)
	b2 = Int((lnValue - b3*m2)/m1)
	b1 = Int((lnValue - b3*m2 - b2*m1)/m0)
	b0 = Mod(lnValue, m0)
RETURN Chr(b0)+Chr(b1)+Chr(b2)+Chr(b3)

FUNCTION num2word(lnValue)
RETURN Chr(MOD(m.lnValue,256)) + CHR(INT(m.lnValue/256))

PROCEDURE declare
	DECLARE INTEGER GetActiveWindow IN user32
	DECLARE INTEGER CloseClipboard IN user32
	DECLARE INTEGER DeleteObject IN gdi32 INTEGER hObject
	DECLARE INTEGER GetWindowDC IN user32 INTEGER hWindow
	DECLARE INTEGER CreateCompatibleDC IN gdi32 INTEGER hdc
	DECLARE INTEGER DeleteDC IN gdi32 INTEGER hdc
	DECLARE INTEGER GlobalFree IN kernel32 INTEGER hMem
	DECLARE INTEGER OpenClipboard IN user32 INTEGER hWindow
	DECLARE INTEGER GetClipboardData IN user32 INTEGER uFormat
	DECLARE INTEGER ReleaseDC IN user32 INTEGER hWindow, INTEGER hdc
	DECLARE INTEGER GlobalAlloc IN kernel32 INTEGER wFlags, INTEGER dwBytes
	DECLARE INTEGER CloseHandle IN kernel32 INTEGER hObject

	DECLARE INTEGER ShellExecute IN shell32;
		INTEGER hWindow, STRING lpOperation,;
		STRING lpFile, STRING lpParameters,;
		STRING lpDirectory, INTEGER nShowCmd

	DECLARE INTEGER GetObject IN gdi32 AS GetObjectA;
		INTEGER hgdiobj, INTEGER cbBuffer, STRING @lpvObject

	DECLARE INTEGER GetObjectType IN gdi32 INTEGER h

	DECLARE RtlZeroMemory IN kernel32 As ZeroMemory;
		INTEGER dest, INTEGER numBytes

	DECLARE INTEGER GetDIBits IN gdi32;
		INTEGER hdc, INTEGER hbmp,;
		INTEGER uStartScan, INTEGER cScanLines,;
		INTEGER lpvBits, STRING @lpbi, INTEGER uUsage

	DECLARE INTEGER CreateFile IN kernel32;
		STRING lpFileName, INTEGER dwDesiredAccess,;
		INTEGER dwShareMode, INTEGER lpSecurityAttr,;
		INTEGER dwCreationDisp, INTEGER dwFlagsAndAttrs,;
		INTEGER hTemplateFile  

Listed functions:

CloseClipboard
CloseHandle
CreateCompatibleDC
CreateFile
DeleteDC
DeleteObject
GetActiveWindow
GetClipboardData
GetDIBits
GetObject
GetObjectType
GetWindowDC
GlobalAlloc
GlobalFree
OpenClipboard
ReleaseDC
ShellExecute
WriteFile

Comment:

The clipboard must stay opened during the procedure and only then closed. The handle to the clipboard content is retrieved via the GetClipboardData call.

If this is a bitmap handle, then the code is invoked that is very much like the Storing screen shot of a form to a bitmap file.

The target file is a 24 bit per pixel bitmap.