Last active
April 22, 2023 09:03
-
-
Save ohaval/f5182a14a8c65cc40d1c4a400bb02389 to your computer and use it in GitHub Desktop.
Save an image from the clipboard to a file
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
I wanted to play with the clipboard via Windows API so I wrote a small program that | |
checks if the clipboard contains an image, and if so saves the image as a .bmp file. | |
This program obtains the handle on the clipboard data, put together all of the | |
information and raw data a .bmp file holds, and writes it to the disk. | |
With small changes it's possible to make this code run in a loop and save an image | |
from the clipboard every X seconds. | |
It's useful to read Microsoft's documentation (for developers) on the clipboard: | |
https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard | |
*/ | |
#include <iostream> | |
#include <fstream> | |
#include <Windows.h> | |
LPSTR pFilename = NULL; | |
int WriteBitmapFile(PBITMAPFILEHEADER pFileHeader, PBITMAPINFOHEADER pInfoHeader, PBYTE pImageData, DWORD dwImageDataSize) { | |
/* | |
The format of a .bmp file is: | |
1) File header | |
2) Info header | |
3) Raw bytes (representing the pixels) | |
(https://en.wikipedia.org/wiki/BMP_file_format) | |
*/ | |
std::ofstream fout; | |
fout.open(pFilename, std::ios::out | std::ios::binary); | |
if (!fout) | |
{ | |
printf("ofstream::open failed (err %d)\n", GetLastError()); | |
return 1; | |
} | |
fout.write((char*)(pFileHeader), sizeof(BITMAPFILEHEADER)); | |
fout.write((char*)(pInfoHeader), sizeof(BITMAPINFOHEADER)); | |
fout.write((char*)(pImageData), dwImageDataSize); | |
fout.close(); | |
return 0; | |
} | |
int SaveFromBitmapInfo(PBITMAPINFO pBitmapInfo) { | |
// This program currently doesn't support other types of compression | |
if (pBitmapInfo->bmiHeader.biCompression != BI_RGB) { | |
printf("[!] Bitmap compression is different from BI_RGB\n"); | |
return 0; | |
} | |
BITMAPFILEHEADER bmfh = { 0 }; | |
bmfh.bfType = 0x4D42; // BMP magic | |
bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); // 14 and 40 bytes respectively | |
bmfh.bfSize = bmfh.bfOffBits + | |
pBitmapInfo->bmiHeader.biWidth * pBitmapInfo->bmiHeader.biHeight * pBitmapInfo->bmiHeader.biBitCount / 8; | |
return WriteBitmapFile(&bmfh, &pBitmapInfo->bmiHeader, (PBYTE)&pBitmapInfo->bmiColors, bmfh.bfSize - bmfh.bfOffBits); | |
} | |
int SaveBitmapFromClipboard() { | |
HANDLE hGlobal = NULL; | |
int rv = 1; | |
if (!IsClipboardFormatAvailable(CF_DIB)) { | |
printf("[!] CF_DIB is not an avialable format\n"); | |
return 1; | |
} | |
printf("[*] BITMAPINFO format is available\n"); | |
if (!OpenClipboard(NULL)) { | |
printf("[!] OpenClipboard failed (err %d)\n", GetLastError()); | |
return 1; | |
} | |
hGlobal = GetClipboardData(CF_DIB); | |
if (hGlobal == NULL) { | |
printf("[!] GetClipboardData failed (err %d)\n", GetLastError()); | |
return 1; | |
} | |
PBITMAPINFO pClipboardData = (PBITMAPINFO)GlobalLock(hGlobal); | |
if (pClipboardData == NULL) { | |
printf("[!] GlobalLock failed (err %d)\n", GetLastError()); | |
return 1; | |
} | |
rv = SaveFromBitmapInfo(pClipboardData); | |
GlobalUnlock(hGlobal); | |
if (!CloseClipboard()) { | |
printf("[!] CloseClipboard failed (err %d)\n", GetLastError()); | |
return 1; | |
} | |
return rv; | |
} | |
int main(int argc, char** argv) { | |
if (argc != 2) { | |
printf("USAGE: SaveImageFromClipboard.exe FILENAME.bmp\n"); | |
exit(1); | |
} | |
pFilename = argv[1]; | |
return SaveBitmapFromClipboard(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Please note this code produces invalid .bmp file in case width*Bpp is not naturally DWORD-aligned.
Example: 24-bit with width=1026. Line stride should be 3080, but writing the image data in one go like this produces an invalid bmp with stride only 3078.
Seemingly Visual Studio has some leniency for such broken bmp's, since at least VS2019 actually can load and display it. mspaint however can not.