Fix clipboard history support in v0.20

This commit is contained in:
f1den committed 2026-08-10 17:37:26 +03:00
1 parent ceb0b5552e
commit 784be4eb67
8 files changed
+278 -25

No files matched your search

+19 -15
View File
@@ -48,65 +48,69 @@ echo Compiler: !CXX_PATH!
echo Target: !TARGET! echo Target: !TARGET!
echo. echo.
echo [1/13] Compiling capture.cpp echo [1/14] Compiling capture.cpp
"!CXX_PATH!" !COMMON! -c src\capture.cpp -o build\capture.o "!CXX_PATH!" !COMMON! -c src\capture.cpp -o build\capture.o
if errorlevel 1 goto :build_failed if errorlevel 1 goto :build_failed
echo [2/13] Compiling image_io.cpp echo [2/14] Compiling image_io.cpp
"!CXX_PATH!" !COMMON! -c src\image_io.cpp -o build\image_io.o "!CXX_PATH!" !COMMON! -c src\image_io.cpp -o build\image_io.o
if errorlevel 1 goto :build_failed if errorlevel 1 goto :build_failed
echo [3/13] Compiling gif_recorder.cpp echo [3/14] Compiling clipboard_history.cpp
"!CXX_PATH!" !COMMON! -c src\clipboard_history.cpp -o build\clipboard_history.o
if errorlevel 1 goto :build_failed
echo [4/14] Compiling gif_recorder.cpp
"!CXX_PATH!" !COMMON! -c src\gif_recorder.cpp -o build\gif_recorder.o "!CXX_PATH!" !COMMON! -c src\gif_recorder.cpp -o build\gif_recorder.o
if errorlevel 1 goto :build_failed if errorlevel 1 goto :build_failed
echo [4/13] Compiling gif_options_window.cpp echo [5/14] Compiling gif_options_window.cpp
"!CXX_PATH!" !COMMON! -c src\gif_options_window.cpp -o build\gif_options_window.o "!CXX_PATH!" !COMMON! -c src\gif_options_window.cpp -o build\gif_options_window.o
if errorlevel 1 goto :build_failed if errorlevel 1 goto :build_failed
echo [5/13] Compiling overlay.cpp echo [6/14] Compiling overlay.cpp
"!CXX_PATH!" !COMMON! -c src\overlay.cpp -o build\overlay.o "!CXX_PATH!" !COMMON! -c src\overlay.cpp -o build\overlay.o
if errorlevel 1 goto :build_failed if errorlevel 1 goto :build_failed
echo [6/13] Compiling notifications.cpp echo [7/14] Compiling notifications.cpp
"!CXX_PATH!" !COMMON! -c src\notifications.cpp -o build\notifications.o "!CXX_PATH!" !COMMON! -c src\notifications.cpp -o build\notifications.o
if errorlevel 1 goto :build_failed if errorlevel 1 goto :build_failed
echo [7/13] Compiling settings.cpp echo [8/14] Compiling settings.cpp
"!CXX_PATH!" !COMMON! -c src\settings.cpp -o build\settings.o "!CXX_PATH!" !COMMON! -c src\settings.cpp -o build\settings.o
if errorlevel 1 goto :build_failed if errorlevel 1 goto :build_failed
echo [8/13] Compiling startup.cpp echo [9/14] Compiling startup.cpp
"!CXX_PATH!" !COMMON! -c src\startup.cpp -o build\startup.o "!CXX_PATH!" !COMMON! -c src\startup.cpp -o build\startup.o
if errorlevel 1 goto :build_failed if errorlevel 1 goto :build_failed
echo [9/13] Compiling settings_window.cpp echo [10/14] Compiling settings_window.cpp
"!CXX_PATH!" !COMMON! -c src\settings_window.cpp -o build\settings_window.o "!CXX_PATH!" !COMMON! -c src\settings_window.cpp -o build\settings_window.o
if errorlevel 1 goto :build_failed if errorlevel 1 goto :build_failed
echo [10/13] Compiling uploader.cpp echo [11/14] Compiling uploader.cpp
"!CXX_PATH!" !COMMON! -c src\uploader.cpp -o build\uploader.o "!CXX_PATH!" !COMMON! -c src\uploader.cpp -o build\uploader.o
if errorlevel 1 goto :build_failed if errorlevel 1 goto :build_failed
echo [11/13] Compiling main.cpp echo [12/14] Compiling main.cpp
"!CXX_PATH!" !COMMON! -c src\main.cpp -o build\main.o "!CXX_PATH!" !COMMON! -c src\main.cpp -o build\main.o
if errorlevel 1 goto :build_failed if errorlevel 1 goto :build_failed
if defined WINDRES_PATH ( if defined WINDRES_PATH (
echo [12/13] Compiling resources echo [13/14] Compiling resources
"!WINDRES_PATH!" -Iinclude -Ires res\app.rc -O coff -o build\app_res.o "!WINDRES_PATH!" -Iinclude -Ires res\app.rc -O coff -o build\app_res.o
if errorlevel 1 goto :build_failed if errorlevel 1 goto :build_failed
set "RES_OBJECT=build\app_res.o" set "RES_OBJECT=build\app_res.o"
) else ( ) else (
echo [12/13] Resources skipped echo [13/14] Resources skipped
echo [WARN] Win64 windres was not found. The EXE will still build, but without the embedded icon/version/manifest. echo [WARN] Win64 windres was not found. The EXE will still build, but without the embedded icon/version/manifest.
echo [WARN] Install the full MSYS2 mingw-w64-x86_64-binutils package to restore resources. echo [WARN] Install the full MSYS2 mingw-w64-x86_64-binutils package to restore resources.
) )
echo [13/13] Linking Win64 executable echo [14/14] Linking Win64 executable
"!CXX_PATH!" -municode -mwindows -static -static-libgcc -static-libstdc++ ^ "!CXX_PATH!" -municode -mwindows -static -static-libgcc -static-libstdc++ ^
-Wl,--gc-sections -Wl,--nxcompat -Wl,--dynamicbase -Wl,--high-entropy-va ^ -Wl,--gc-sections -Wl,--nxcompat -Wl,--dynamicbase -Wl,--high-entropy-va ^
build\capture.o build\image_io.o build\gif_recorder.o build\gif_options_window.o ^ build\capture.o build\image_io.o build\clipboard_history.o build\gif_recorder.o build\gif_options_window.o ^
build\overlay.o build\notifications.o ^ build\overlay.o build\notifications.o ^
build\settings.o build\startup.o build\settings_window.o build\uploader.o ^ build\settings.o build\startup.o build\settings_window.o build\uploader.o ^
build\main.o !RES_OBJECT! ^ build\main.o !RES_OBJECT! ^
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include "config.hpp"
#include <windows.h>
#include <vector>
namespace clipboard_history {
// Publishes an encoded bitmap stream through Windows.ApplicationModel.DataTransfer
// so Windows 10/11 clipboard history can ingest it. This intentionally happens
// before the richer Win32 clipboard payload is installed.
[[nodiscard]] bool publishBitmap(const std::vector<BYTE>& encodedBitmap,
DWORD settleMilliseconds = 450);
} // namespace clipboard_history
+1
View File
@@ -12,6 +12,7 @@
[[nodiscard]] bool saveBitmapAsJpeg(Gdiplus::Bitmap& bitmap, const std::wstring& path, int quality); [[nodiscard]] bool saveBitmapAsJpeg(Gdiplus::Bitmap& bitmap, const std::wstring& path, int quality);
[[nodiscard]] bool copyBitmapToClipboard(HWND owner, Gdiplus::Bitmap& bitmap); [[nodiscard]] bool copyBitmapToClipboard(HWND owner, Gdiplus::Bitmap& bitmap);
[[nodiscard]] bool encodeBitmapAsPng(Gdiplus::Bitmap& bitmap, std::vector<BYTE>& output); [[nodiscard]] bool encodeBitmapAsPng(Gdiplus::Bitmap& bitmap, std::vector<BYTE>& output);
[[nodiscard]] bool encodeBitmapForClipboardHistory(Gdiplus::Bitmap& bitmap, std::vector<BYTE>& output);
// Reserves a non-existing incremented file name with CREATE_NEW. The returned // Reserves a non-existing incremented file name with CREATE_NEW. The returned
// empty file belongs to the caller and may safely be filled/overwritten. // empty file belongs to the caller and may safely be filled/overwritten.
+4 -4
View File
@@ -3,8 +3,8 @@ IDI_APP_ICON ICON "nightshot.ico"
IDR_APP_MANIFEST RT_MANIFEST "app.manifest" IDR_APP_MANIFEST RT_MANIFEST "app.manifest"
1 VERSIONINFO 1 VERSIONINFO
FILEVERSION 0,19,0,0 FILEVERSION 0,20,0,0
PRODUCTVERSION 0,19,0,0 PRODUCTVERSION 0,20,0,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
FILEFLAGS 0x0L FILEFLAGS 0x0L
FILEOS 0x40004L FILEOS 0x40004L
@@ -17,11 +17,11 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "NightSync" VALUE "CompanyName", "NightSync"
VALUE "FileDescription", "NightShot Win64 screenshot utility" VALUE "FileDescription", "NightShot Win64 screenshot utility"
VALUE "FileVersion", "0.19.0" VALUE "FileVersion", "0.20.0"
VALUE "InternalName", "NightShot" VALUE "InternalName", "NightShot"
VALUE "OriginalFilename", "NightShot.exe" VALUE "OriginalFilename", "NightShot.exe"
VALUE "ProductName", "NightShot" VALUE "ProductName", "NightShot"
VALUE "ProductVersion", "0.19.0" VALUE "ProductVersion", "0.20.0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"
+174
View File
@@ -0,0 +1,174 @@
#include "clipboard_history.hpp"
#include <objidl.h>
#include <roapi.h>
#include <shcore.h>
#include <windows.applicationmodel.datatransfer.h>
#include <windows.storage.streams.h>
#include <wrl/client.h>
#include <wrl/wrappers/corewrappers.h>
#include <cstring>
namespace clipboard_history {
namespace {
using Microsoft::WRL::ComPtr;
using Microsoft::WRL::Wrappers::HStringReference;
using ABI::Windows::ApplicationModel::DataTransfer::IClipboardStatics;
using ABI::Windows::ApplicationModel::DataTransfer::IClipboardStatics2;
using ABI::Windows::ApplicationModel::DataTransfer::IClipboardContentOptions;
using ABI::Windows::ApplicationModel::DataTransfer::IDataPackage;
using ABI::Windows::Storage::Streams::IRandomAccessStream;
using ABI::Windows::Storage::Streams::IRandomAccessStreamReference;
using ABI::Windows::Storage::Streams::IRandomAccessStreamReferenceStatics;
struct RuntimeScope {
bool uninitialize = false;
RuntimeScope() {
const HRESULT result = RoInitialize(RO_INIT_SINGLETHREADED);
if (result == S_OK || result == S_FALSE) {
uninitialize = true;
} else if (result == RPC_E_CHANGED_MODE) {
// The caller already initialized COM/WinRT using another apartment.
// Static WinRT clipboard APIs remain usable on that thread.
} else {
initializationResult = result;
}
}
~RuntimeScope() {
if (uninitialize) RoUninitialize();
}
HRESULT initializationResult = S_OK;
[[nodiscard]] bool ready() const {
return SUCCEEDED(initializationResult);
}
};
HRESULT makeRandomAccessReference(const std::vector<BYTE>& bytes,
ComPtr<IRandomAccessStreamReference>& reference) {
if (bytes.empty()) return E_INVALIDARG;
HGLOBAL memory = GlobalAlloc(GMEM_MOVEABLE, bytes.size());
if (!memory) return E_OUTOFMEMORY;
void* destination = GlobalLock(memory);
if (!destination) {
const HRESULT error = HRESULT_FROM_WIN32(GetLastError());
GlobalFree(memory);
return error;
}
std::memcpy(destination, bytes.data(), bytes.size());
GlobalUnlock(memory);
ComPtr<IStream> stream;
HRESULT result = CreateStreamOnHGlobal(memory, TRUE, stream.ReleaseAndGetAddressOf());
if (FAILED(result)) {
GlobalFree(memory);
return result;
}
LARGE_INTEGER zero{};
result = stream->Seek(zero, STREAM_SEEK_SET, nullptr);
if (FAILED(result)) return result;
ComPtr<IRandomAccessStream> randomAccessStream;
result = CreateRandomAccessStreamOverStream(
stream.Get(), BSOS_DEFAULT,
IID_PPV_ARGS(randomAccessStream.ReleaseAndGetAddressOf()));
if (FAILED(result)) return result;
ComPtr<IRandomAccessStreamReferenceStatics> referenceFactory;
result = RoGetActivationFactory(
HStringReference(RuntimeClass_Windows_Storage_Streams_RandomAccessStreamReference).Get(),
IID_PPV_ARGS(referenceFactory.ReleaseAndGetAddressOf()));
if (FAILED(result)) return result;
return referenceFactory->CreateFromStream(
randomAccessStream.Get(), reference.ReleaseAndGetAddressOf());
}
} // namespace
bool publishBitmap(const std::vector<BYTE>& encodedBitmap, DWORD settleMilliseconds) {
if (encodedBitmap.empty()) return false;
RuntimeScope runtime;
if (!runtime.ready()) return false;
ComPtr<IRandomAccessStreamReference> streamReference;
HRESULT result = makeRandomAccessReference(encodedBitmap, streamReference);
if (FAILED(result)) return false;
ComPtr<IInspectable> inspectablePackage;
result = RoActivateInstance(
HStringReference(RuntimeClass_Windows_ApplicationModel_DataTransfer_DataPackage).Get(),
inspectablePackage.ReleaseAndGetAddressOf());
if (FAILED(result)) return false;
ComPtr<IDataPackage> package;
result = inspectablePackage.As(&package);
if (FAILED(result)) return false;
result = package->SetBitmap(streamReference.Get());
if (FAILED(result)) return false;
ComPtr<IClipboardStatics> clipboard;
result = RoGetActivationFactory(
HStringReference(RuntimeClass_Windows_ApplicationModel_DataTransfer_Clipboard).Get(),
IID_PPV_ARGS(clipboard.ReleaseAndGetAddressOf()));
if (FAILED(result)) return false;
bool contentAccepted = false;
// Windows 10 1809+ exposes SetContentWithOptions. Unlike SetContent, this
// API can be used when NightShot is no longer the foreground window (most
// importantly after an asynchronous GIF encode), and lets us explicitly
// mark the compact bitmap representation as history-eligible.
ComPtr<IClipboardStatics2> clipboard2;
result = RoGetActivationFactory(
HStringReference(RuntimeClass_Windows_ApplicationModel_DataTransfer_Clipboard).Get(),
IID_PPV_ARGS(clipboard2.ReleaseAndGetAddressOf()));
if (SUCCEEDED(result) && clipboard2) {
ComPtr<IInspectable> inspectableOptions;
result = RoActivateInstance(
HStringReference(RuntimeClass_Windows_ApplicationModel_DataTransfer_ClipboardContentOptions).Get(),
inspectableOptions.ReleaseAndGetAddressOf());
if (SUCCEEDED(result) && inspectableOptions) {
ComPtr<IClipboardContentOptions> options;
result = inspectableOptions.As(&options);
if (SUCCEEDED(result) && options) {
result = options->put_IsAllowedInHistory(true);
if (SUCCEEDED(result)) {
boolean accepted = 0;
result = clipboard2->SetContentWithOptions(
package.Get(), options.Get(), &accepted);
contentAccepted = SUCCEEDED(result) && accepted != 0;
}
}
}
}
// Compatibility fallback for Windows versions that do not expose the
// options API. SetContent requires the application to be foreground, so
// this path is intentionally second choice.
if (!contentAccepted) {
result = clipboard->SetContent(package.Get());
if (FAILED(result)) return false;
contentAccepted = true;
}
// Materialize the DataPackage now, while all stream objects are alive.
result = clipboard->Flush();
if (FAILED(result)) return false;
// Clipboard History consumes clipboard updates asynchronously. Keep this
// history-friendly item current long enough for cbdhsvc to observe it before
// NightShot replaces it with the richer Win32 PNG/DIB/GIF payload.
if (settleMilliseconds != 0) Sleep(settleMilliseconds);
return true;
}
} // namespace clipboard_history
+17 -4
View File
@@ -1,6 +1,7 @@
#include "gif_recorder.hpp" #include "gif_recorder.hpp"
#include "app_messages.hpp" #include "app_messages.hpp"
#include "clipboard_history.hpp"
#include "image_io.hpp" #include "image_io.hpp"
#include "settings.hpp" #include "settings.hpp"
@@ -262,13 +263,25 @@ bool copyGifToClipboard(const Session& session) {
const std::vector<BYTE> gif = readAllBytes(session.path); const std::vector<BYTE> gif = readAllBytes(session.path);
if (gif.empty()) return false; if (gif.empty()) return false;
std::vector<BYTE> historyPreview;
bool historyPublished = false;
if (!session.firstFrame.empty() && session.width > 0 && session.height > 0 &&
session.stride >= session.width * 4) {
Gdiplus::Bitmap firstFrame(session.width, session.height, session.stride,
PixelFormat32bppARGB,
const_cast<BYTE*>(session.firstFrame.data()));
historyPublished = firstFrame.GetLastStatus() == Gdiplus::Ok &&
encodeBitmapForClipboardHistory(firstFrame, historyPreview) &&
clipboard_history::publishBitmap(historyPreview);
}
HGLOBAL gifGeneric = globalBytes(gif.data(), gif.size()); HGLOBAL gifGeneric = globalBytes(gif.data(), gif.size());
HGLOBAL gifMime = globalBytes(gif.data(), gif.size()); HGLOBAL gifMime = globalBytes(gif.data(), gif.size());
HGLOBAL files = dropFiles(session.path); HGLOBAL files = dropFiles(session.path);
HGLOBAL dibV5 = firstFrameDibV5(session); HGLOBAL dibV5 = firstFrameDibV5(session);
HGLOBAL dib = firstFrameDib24(session); HGLOBAL dib = firstFrameDib24(session);
HBITMAP bitmap = firstFrameBitmap(session); HBITMAP bitmap = firstFrameBitmap(session);
HGLOBAL includeInHistory = serializedDword(1); HGLOBAL includeInHistory = serializedDword(historyPublished ? 0u : 1u);
if (!gifGeneric && !gifMime && !files && !dibV5 && !dib && !bitmap) { if (!gifGeneric && !gifMime && !files && !dibV5 && !dib && !bitmap) {
if (includeInHistory) GlobalFree(includeInHistory); if (includeInHistory) GlobalFree(includeInHistory);
return false; return false;
@@ -322,9 +335,9 @@ bool copyGifToClipboard(const Session& session) {
transferred = true; transferred = true;
} }
// Explicitly ask Windows 10/11 clipboard monitoring to retain this item in // The WinRT history proxy above carries the first frame. Keep the rich
// Win+V. The first frame is also published as standard bitmap formats so // animated GIF replacement out of history when that succeeded; otherwise
// history has a format it can display even when it ignores the custom GIF. // fall back to the documented Win32 history opt-in.
const UINT historyFormat = RegisterClipboardFormatW(L"CanIncludeInClipboardHistory"); const UINT historyFormat = RegisterClipboardFormatW(L"CanIncludeInClipboardHistory");
if (includeInHistory && historyFormat && SetClipboardData(historyFormat, includeInHistory)) { if (includeInHistory && historyFormat && SetClipboardData(historyFormat, includeInHistory)) {
includeInHistory = nullptr; includeInHistory = nullptr;
+48 -1
View File
@@ -1,4 +1,5 @@
#include "image_io.hpp" #include "image_io.hpp"
#include "clipboard_history.hpp"
#include <commdlg.h> #include <commdlg.h>
#include <shlobj.h> #include <shlobj.h>
#include <filesystem> #include <filesystem>
@@ -223,6 +224,39 @@ bool openClipboardWithRetry(HWND owner) {
return false; return false;
} }
bool makeClipboardHistoryPreview(Gdiplus::Bitmap& source, std::vector<BYTE>& output) {
output.clear();
const UINT sourceWidth = source.GetWidth();
const UINT sourceHeight = source.GetHeight();
if (sourceWidth == 0 || sourceHeight == 0) return false;
// Win+V has a small per-item budget. Keep the history representation at
// most 1280x720; the actual clipboard remains full resolution below.
constexpr UINT kMaximumWidth = 1280;
constexpr UINT kMaximumHeight = 720;
const double scale = std::min(
1.0, std::min(static_cast<double>(kMaximumWidth) / sourceWidth,
static_cast<double>(kMaximumHeight) / sourceHeight));
if (scale >= 0.999) return encodeBitmapAsPng(source, output);
const UINT width = std::max<UINT>(1, static_cast<UINT>(sourceWidth * scale + 0.5));
const UINT height = std::max<UINT>(1, static_cast<UINT>(sourceHeight * scale + 0.5));
Gdiplus::Bitmap preview(static_cast<INT>(width), static_cast<INT>(height),
PixelFormat32bppARGB);
if (preview.GetLastStatus() != Gdiplus::Ok) return false;
Gdiplus::Graphics graphics(&preview);
graphics.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
graphics.Clear(Gdiplus::Color(255, 255, 255, 255));
graphics.SetCompositingMode(Gdiplus::CompositingModeSourceOver);
graphics.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
graphics.SetPixelOffsetMode(Gdiplus::PixelOffsetModeHighQuality);
if (graphics.DrawImage(&source, 0, 0, static_cast<INT>(width),
static_cast<INT>(height)) != Gdiplus::Ok) return false;
graphics.Flush(Gdiplus::FlushIntentionSync);
return encodeBitmapAsPng(preview, output);
}
HGLOBAL serializedDword(DWORD value) { HGLOBAL serializedDword(DWORD value) {
HGLOBAL memory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DWORD)); HGLOBAL memory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DWORD));
if (!memory) return nullptr; if (!memory) return nullptr;
@@ -237,15 +271,28 @@ HGLOBAL serializedDword(DWORD value) {
} }
} // namespace } // namespace
bool encodeBitmapForClipboardHistory(Gdiplus::Bitmap& bitmap, std::vector<BYTE>& output) {
return makeClipboardHistoryPreview(bitmap, output);
}
bool copyBitmapToClipboard(HWND owner, Gdiplus::Bitmap& bitmap) { bool copyBitmapToClipboard(HWND owner, Gdiplus::Bitmap& bitmap) {
OpaqueBgraImage image = renderOpaqueBgra(bitmap); OpaqueBgraImage image = renderOpaqueBgra(bitmap);
if (!image.valid()) return false; if (!image.valid()) return false;
std::vector<BYTE> historyPreview;
const bool historyPublished =
encodeBitmapForClipboardHistory(bitmap, historyPreview) &&
clipboard_history::publishBitmap(historyPreview);
HGLOBAL png = makePng(image); HGLOBAL png = makePng(image);
HGLOBAL dibV5 = makeDibV5(image); HGLOBAL dibV5 = makeDibV5(image);
HGLOBAL dib = makeDib24(image); HGLOBAL dib = makeDib24(image);
HBITMAP nativeBitmap = makeClipboardBitmap(image); HBITMAP nativeBitmap = makeClipboardBitmap(image);
HGLOBAL includeInHistory = serializedDword(1); // If the WinRT history item was accepted, explicitly keep the following
// rich Win32 replacement out of history so it cannot create a duplicate or
// displace the compact preview. Fall back to the documented Win32 opt-in
// when WinRT is unavailable.
HGLOBAL includeInHistory = serializedDword(historyPublished ? 0u : 1u);
if (!png && !dibV5 && !dib && !nativeBitmap) { if (!png && !dibV5 && !dib && !nativeBitmap) {
if (includeInHistory) GlobalFree(includeInHistory); if (includeInHistory) GlobalFree(includeInHistory);
return false; return false;
+1 -1
View File
@@ -209,7 +209,7 @@ std::unique_ptr<Result> performUpload(const Job& job) {
} }
InternetHandle session(WinHttpOpen( InternetHandle session(WinHttpOpen(
L"NightShot/0.19", WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, L"NightShot/0.20", WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0)); WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0));
if (!session.value) { if (!session.value) {
result->message = L"WinHttpOpen: " + win32Error(GetLastError()); result->message = L"WinHttpOpen: " + win32Error(GetLastError());