Skip to content

Instantly share code, notes, and snippets.

@ohaval
Last active April 22, 2023 09:03
Show Gist options
  • Save ohaval/f5182a14a8c65cc40d1c4a400bb02389 to your computer and use it in GitHub Desktop.
Save ohaval/f5182a14a8c65cc40d1c4a400bb02389 to your computer and use it in GitHub Desktop.
Save an image from the clipboard to a file
/*
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();
}
@ohaval
Copy link
Author

ohaval commented Apr 22, 2023

@tamlin-mike Thanks for the note!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment