Created
March 1, 2020 11:45
-
-
Save ifree/a57eaafcc802ba5eed9f12287b244d1b to your computer and use it in GitHub Desktop.
paste clibpard image to web service
This file contains hidden or 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
#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