Fix clipboard history support in v0.20
This commit is contained in:
1 parent
ceb0b5552e
commit
784be4eb67
8 files changed
+278
-25
No files matched your search
@@ -48,65 +48,69 @@ echo Compiler: !CXX_PATH!
|
||||
echo Target: !TARGET!
|
||||
echo.
|
||||
|
||||
echo [1/13] Compiling capture.cpp
|
||||
echo [1/14] Compiling capture.cpp
|
||||
"!CXX_PATH!" !COMMON! -c src\capture.cpp -o build\capture.o
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
if errorlevel 1 goto :build_failed
|
||||
|
||||
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
|
||||
if errorlevel 1 goto :build_failed
|
||||
set "RES_OBJECT=build\app_res.o"
|
||||
) 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] 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++ ^
|
||||
-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\settings.o build\startup.o build\settings_window.o build\uploader.o ^
|
||||
build\main.o !RES_OBJECT! ^
|
||||
|
||||
@@ -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
|
||||
@@ -12,6 +12,7 @@
|
||||
[[nodiscard]] bool saveBitmapAsJpeg(Gdiplus::Bitmap& bitmap, const std::wstring& path, int quality);
|
||||
[[nodiscard]] bool copyBitmapToClipboard(HWND owner, Gdiplus::Bitmap& bitmap);
|
||||
[[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
|
||||
// empty file belongs to the caller and may safely be filled/overwritten.
|
||||
|
||||
+4
-4
@@ -3,8 +3,8 @@ IDI_APP_ICON ICON "nightshot.ico"
|
||||
IDR_APP_MANIFEST RT_MANIFEST "app.manifest"
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION 0,19,0,0
|
||||
PRODUCTVERSION 0,19,0,0
|
||||
FILEVERSION 0,20,0,0
|
||||
PRODUCTVERSION 0,20,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
FILEFLAGS 0x0L
|
||||
FILEOS 0x40004L
|
||||
@@ -17,11 +17,11 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "NightSync"
|
||||
VALUE "FileDescription", "NightShot Win64 screenshot utility"
|
||||
VALUE "FileVersion", "0.19.0"
|
||||
VALUE "FileVersion", "0.20.0"
|
||||
VALUE "InternalName", "NightShot"
|
||||
VALUE "OriginalFilename", "NightShot.exe"
|
||||
VALUE "ProductName", "NightShot"
|
||||
VALUE "ProductVersion", "0.19.0"
|
||||
VALUE "ProductVersion", "0.20.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -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
@@ -1,6 +1,7 @@
|
||||
#include "gif_recorder.hpp"
|
||||
|
||||
#include "app_messages.hpp"
|
||||
#include "clipboard_history.hpp"
|
||||
#include "image_io.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
@@ -262,13 +263,25 @@ bool copyGifToClipboard(const Session& session) {
|
||||
const std::vector<BYTE> gif = readAllBytes(session.path);
|
||||
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 gifMime = globalBytes(gif.data(), gif.size());
|
||||
HGLOBAL files = dropFiles(session.path);
|
||||
HGLOBAL dibV5 = firstFrameDibV5(session);
|
||||
HGLOBAL dib = firstFrameDib24(session);
|
||||
HBITMAP bitmap = firstFrameBitmap(session);
|
||||
HGLOBAL includeInHistory = serializedDword(1);
|
||||
HGLOBAL includeInHistory = serializedDword(historyPublished ? 0u : 1u);
|
||||
if (!gifGeneric && !gifMime && !files && !dibV5 && !dib && !bitmap) {
|
||||
if (includeInHistory) GlobalFree(includeInHistory);
|
||||
return false;
|
||||
@@ -322,9 +335,9 @@ bool copyGifToClipboard(const Session& session) {
|
||||
transferred = true;
|
||||
}
|
||||
|
||||
// Explicitly ask Windows 10/11 clipboard monitoring to retain this item in
|
||||
// Win+V. The first frame is also published as standard bitmap formats so
|
||||
// history has a format it can display even when it ignores the custom GIF.
|
||||
// The WinRT history proxy above carries the first frame. Keep the rich
|
||||
// animated GIF replacement out of history when that succeeded; otherwise
|
||||
// fall back to the documented Win32 history opt-in.
|
||||
const UINT historyFormat = RegisterClipboardFormatW(L"CanIncludeInClipboardHistory");
|
||||
if (includeInHistory && historyFormat && SetClipboardData(historyFormat, includeInHistory)) {
|
||||
includeInHistory = nullptr;
|
||||
|
||||
+48
-1
@@ -1,4 +1,5 @@
|
||||
#include "image_io.hpp"
|
||||
#include "clipboard_history.hpp"
|
||||
#include <commdlg.h>
|
||||
#include <shlobj.h>
|
||||
#include <filesystem>
|
||||
@@ -223,6 +224,39 @@ bool openClipboardWithRetry(HWND owner) {
|
||||
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 memory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DWORD));
|
||||
if (!memory) return nullptr;
|
||||
@@ -237,15 +271,28 @@ HGLOBAL serializedDword(DWORD value) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool encodeBitmapForClipboardHistory(Gdiplus::Bitmap& bitmap, std::vector<BYTE>& output) {
|
||||
return makeClipboardHistoryPreview(bitmap, output);
|
||||
}
|
||||
|
||||
bool copyBitmapToClipboard(HWND owner, Gdiplus::Bitmap& bitmap) {
|
||||
OpaqueBgraImage image = renderOpaqueBgra(bitmap);
|
||||
if (!image.valid()) return false;
|
||||
|
||||
std::vector<BYTE> historyPreview;
|
||||
const bool historyPublished =
|
||||
encodeBitmapForClipboardHistory(bitmap, historyPreview) &&
|
||||
clipboard_history::publishBitmap(historyPreview);
|
||||
|
||||
HGLOBAL png = makePng(image);
|
||||
HGLOBAL dibV5 = makeDibV5(image);
|
||||
HGLOBAL dib = makeDib24(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 (includeInHistory) GlobalFree(includeInHistory);
|
||||
return false;
|
||||
|
||||
+1
-1
@@ -209,7 +209,7 @@ std::unique_ptr<Result> performUpload(const Job& job) {
|
||||
}
|
||||
|
||||
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));
|
||||
if (!session.value) {
|
||||
result->message = L"WinHttpOpen: " + win32Error(GetLastError());
|
||||
|
||||
Reference in new issue
Block a user