Skip to content

Instantly share code, notes, and snippets.

@ifree
Created March 1, 2020 11:45
Show Gist options
  • Save ifree/a57eaafcc802ba5eed9f12287b244d1b to your computer and use it in GitHub Desktop.
Save ifree/a57eaafcc802ba5eed9f12287b244d1b to your computer and use it in GitHub Desktop.
paste clibpard image to web service
#include "pch.h"
#include "clipaste_api.h"
//#define WIN32_LEAN_AND_MEAN // GDI+ don't need this
// Windows Header Files
#include <windows.h>
#include <gdiplus.h>
#include <winhttp.h>
using namespace Gdiplus;
#include <cstdint>
#include <vector>
#include <cassert>
class GDIServer {
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
public:
GDIServer() {
// Initialize GDI+.
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
}
~GDIServer() { GdiplusShutdown(gdiplusToken); }
};
class ClipboardOperation {
bool success_ = false;
public:
ClipboardOperation() { success_ = OpenClipboard(nullptr); }
~ClipboardOperation() {
if (success_) {
CloseClipboard();
}
}
bool OK() const { return success_; }
};
class HTTPClient {
HINTERNET session_ = NULL, connection_ = NULL, request_ = NULL;
public:
HTTPClient(const wchar_t *server, int port) {
session_ = WinHttpOpen(
L"Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko",
WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,
NULL,
WINHTTP_NO_PROXY_BYPASS,
0);
if (session_) {
connection_ = WinHttpConnect(session_, server, port, 0);
}
}
bool Request(const wchar_t *path,
uint8_t *data,
size_t size,
std::vector<uint8_t> &response) {
if (!connection_)
return false;
request_ = WinHttpOpenRequest(connection_,
L"POST",
path,
nullptr,
nullptr,
WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_REFRESH);
// clang-format off
const char *boundary_head =
"------b0undary\r\n"
"Content-Disposition: form-data; name=\"image\"; filename=\"dummy.png\"\r\n"
"Content-Type: image/png\r\n\r\n";
const char* boundary_tail =
"\r\n------b0undary--\r\n";
WinHttpAddRequestHeaders(
request_,
L"Content-Type: multipart/form-data; boundary=----b0undary",
-1L,
WINHTTP_ADDREQ_FLAG_ADD);
// clang-format on
bool ok = WinHttpSendRequest(request_,
WINHTTP_NO_ADDITIONAL_HEADERS,
0,
WINHTTP_NO_REQUEST_DATA,
0,
lstrlenA(boundary_head) +
lstrlenA(boundary_tail) + size,
NULL);
if (ok) {
DWORD bytes_written = 0;
ok = WinHttpWriteData(
request_, boundary_head, lstrlenA(boundary_head), nullptr);
assert(ok);
ok = WinHttpWriteData(request_, data, size, &bytes_written);
assert(ok && bytes_written == size);
ok = WinHttpWriteData(
request_, boundary_tail, lstrlenA(boundary_tail), nullptr);
assert(ok);
if (ok) {
ok = WinHttpReceiveResponse(request_, 0);
assert(ok);
DWORD bytes_to_read;
ok = WinHttpQueryDataAvailable(request_, &bytes_to_read);
assert(ok);
if (ok) {
response.resize(bytes_to_read);
ok = WinHttpReadData(request_,
response.data(),
response.size(),
&bytes_to_read);
}
} else {
printf_s("WriteReq Error: %d", GetLastError());
}
} else {
printf_s("SendReq Error: %d", GetLastError());
}
return ok;
}
~HTTPClient() {
if (request_) {
WinHttpCloseHandle(request_);
}
if (connection_) {
WinHttpCloseHandle(connection_);
}
if (session_) {
WinHttpCloseHandle(session_);
}
}
};
bool upload_png(const wchar_t *server,
int port,
const wchar_t *path,
uint8_t *data,
size_t size,
std::vector<uint8_t> &response) {
HTTPClient client{server, port};
return client.Request(path, data, size, response);
}
int GetEncoderClsid(const WCHAR *format, CLSID *pClsid) {
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo *pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if (size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo *)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j) {
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) {
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}
bool ClipasteAPI IsClipboardContainsImage() {
return IsClipboardFormatAvailable(CF_BITMAP);
}
void ClipasteAPI PasteClipboardToServer(const wchar_t *addr, int port) {
if (!IsClipboardContainsImage())
return;
ClipboardOperation _{};
if (!_.OK()) {
return;
}
static GDIServer gdi_server;
HANDLE img_ref = GetClipboardData(CF_BITMAP);
if (img_ref != nullptr) {
// windows can convert bitmap to dib automatically
img_ref = GetClipboardData(CF_DIBV5);
//img_ref = GetClipboardData(CF_DIB);
void *img_mem = GlobalLock(img_ref);
if (img_mem != nullptr) {
// gdi+ solution to convert clipboard image to png
Bitmap *bmp = Bitmap::FromBITMAPINFO(
reinterpret_cast<const BITMAPINFO *>(img_mem),
reinterpret_cast<uint8_t *>(img_mem) + sizeof(BITMAPV5HEADER));
CLSID encoderClsid;
GetEncoderClsid(L"image/png", &encoderClsid);
IStream *istream = nullptr;
HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, &istream);
assert(SUCCEEDED(hr));
bmp->Save(istream, &encoderClsid, nullptr);
HGLOBAL hg = NULL;
hr = GetHGlobalFromStream(istream, &hg);
assert(SUCCEEDED(hr));
std::vector<BYTE> memory_store;
size_t bufsize = GlobalSize(hg);
memory_store.resize(bufsize);
// copying
void *pimage = GlobalLock(hg);
if (pimage) {
memcpy(&memory_store[0], pimage, bufsize);
GlobalUnlock(hg);
std::vector<uint8_t> response{};
// upload!
if (upload_png(addr,
port,
L"/",
memory_store.data(),
memory_store.size(),
response)) {
HGLOBAL text_mem =
GlobalAlloc(GMEM_MOVEABLE, response.size());
if (text_mem) {
memcpy(GlobalLock(text_mem),
response.data(),
response.size());
GlobalUnlock(text_mem);
EmptyClipboard();
SetClipboardData(CF_TEXT, text_mem);
}
}
}
istream->Release();
delete bmp;
GlobalUnlock(img_mem);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment