Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f11bfff784 | ||
|
|
78aea46fb3 | ||
|
|
1549a89dc3 | ||
|
|
47382cdd86 | ||
|
|
49ae0ad157 | ||
|
|
f8c05fd0ce | ||
|
|
d80fad6e48 |
No files matched your search
+1
-1
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
project(KSwitcher VERSION 0.6.86 LANGUAGES CXX RC)
|
||||
project(KSwitcher VERSION 0.6.88 LANGUAGES CXX RC)
|
||||
if(NOT WIN32)
|
||||
message(FATAL_ERROR "KSwitcher uses native Windows APIs")
|
||||
endif()
|
||||
|
||||
+4
-1
@@ -31,7 +31,8 @@ Far Manager є зразком для оформлення файлового м
|
||||
Промпт і текст команди вирівняні по спільній базовій лінії.
|
||||
- У віддаленій панелі Enter запускає введену команду PowerShell на
|
||||
спареному ПК в поточному каталозі цієї панелі. Вивід і код завершення
|
||||
з’являються в терміналі Files. Кожна команда запускається окремо,
|
||||
з’являються в терміналі Files як текст. Службовий прогрес PowerShell
|
||||
приховано. Кожна команда запускається окремо,
|
||||
тому змінні середовища й `cd` між командами не зберігаються. Escape
|
||||
в порожньому командному рядку скасовує поточну віддалену команду.
|
||||
- На ПК, який приймає команди, увімкніть **Options → Command line →
|
||||
@@ -42,6 +43,8 @@ Far Manager є зразком для оформлення файлового м
|
||||
- Ctrl+F вставляє повний шлях елемента під курсором активної панелі в позицію
|
||||
курсора командного рядка або замінює виділення. Подвійні лапки додаються
|
||||
лише до шляхів із пробілами. Для `..` вставляється шлях батьківської папки.
|
||||
- Ctrl+Enter вставляє лише назву файла або папки під курсором активної панелі
|
||||
за тими ж правилами вставлення й лапок. Рядок `..` пропускається.
|
||||
- Ctrl+C копіює весь введений у командний рядок текст, навіть коли виділено
|
||||
лише його частину.
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,5 +1,18 @@
|
||||
# Latest Windows release
|
||||
|
||||
## v0.6.88 — 2026-11-16
|
||||
|
||||
In Files, Ctrl+Enter inserts the name of the file or folder under the active pane cursor into the command line. An open Files window can still request the LAN peer list after KSwitcher updates and restarts.
|
||||
|
||||
**Українською.** У Files Ctrl+Enter вставляє в командний рядок назву файла або папки під курсором активної панелі. Відкрите вікно Files і далі може отримувати список LAN-комп’ютерів після оновлення та автоматичного перезапуску KSwitcher.
|
||||
|
||||
|
||||
## v0.6.87 — 2026-11-15
|
||||
|
||||
Remote PowerShell in Files now displays errors as text and hides progress metadata. Update the PC that runs the commands to remove raw CLIXML from terminal output. This release supersedes v0.6.86.
|
||||
|
||||
**Українською.** Віддалений PowerShell у Files тепер показує помилки як текст і приховує службові повідомлення прогресу. Щоб прибрати сирий CLIXML із термінала, оновіть ПК, який виконує команди. Цей реліз замінює v0.6.86.
|
||||
|
||||
## v0.6.86 — 2026-11-15
|
||||
|
||||
The Files editor applies its palette before the text area appears. In a LAN pane, enter a PowerShell command to run it in that pane’s directory on the paired PC. The Files terminal displays output and the exit code. On the remote PC, enable **Allow paired PCs to run PowerShell commands here** in **Files → Options → Command line → Command history**. Both PCs need v0.6.86 or later.
|
||||
|
||||
@@ -1 +1 @@
|
||||
70deb7c661dc7eb669ced086ebd85458a2c844125992bfa01e9ac7d745329b85 KSwitcher-win-x64.zip
|
||||
d48e590580801a39a08ac8f5192983610e0a3fec9e5b0cf7f01a40a063cbed78 KSwitcher-win-x64.zip
|
||||
+15
-15
@@ -1,26 +1,24 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"version": "0.6.86",
|
||||
"files": [
|
||||
{
|
||||
"path": "KSwitcher.exe",
|
||||
"sha256": "119d6a114c79ebef8461eeaa8f19b30d4f87e5bbe0fdcdea30dbc30d7e33a570",
|
||||
"size": 2461696
|
||||
"sha256": "aa542387388904d2a8df98bdef0e0b25b12aa294ded279f3308553d762086fee",
|
||||
"size": 5852660
|
||||
},
|
||||
{
|
||||
"path": "KSwitcher.Config.exe",
|
||||
"sha256": "d943fd063fa5f6e96d31c4300d701426e24798080a778586692117def3cdb4bd",
|
||||
"size": 2438656
|
||||
"sha256": "c128e8affc7485ba475228ca9bee6915948208405bd84110ffc776ebf8a3d0da",
|
||||
"size": 5917446
|
||||
},
|
||||
{
|
||||
"path": "KSwitcher.Screenshot.exe",
|
||||
"sha256": "d16b8a889225ed61e7f4b8331e4bf7a3354d2a9a8957c375c8bb0e4fd408b555",
|
||||
"size": 3050496
|
||||
"sha256": "82397c1e4d73415449ba7389a716d6e1ec4dab2310b1471369f467435b771762",
|
||||
"size": 5747437
|
||||
},
|
||||
{
|
||||
"path": "KSwitcher.Files.exe",
|
||||
"sha256": "f23160a531fbf800c512e76c9b171528126a1a8ed1f1f61b241e24ba66d0b8cb",
|
||||
"size": 3659264
|
||||
"sha256": "c86b7ff32468bafa6f658a810f475b557f47a23ba42a1739db09d7eeab6fb533",
|
||||
"size": 8159101
|
||||
},
|
||||
{
|
||||
"path": "WebView2Loader.dll",
|
||||
@@ -44,8 +42,8 @@
|
||||
},
|
||||
{
|
||||
"path": "terminal/terminal.js",
|
||||
"sha256": "d60e6ff8948b6616695297695b05ef6e74b497a8e32e6031fc2e4fe2cca39ca7",
|
||||
"size": 4167
|
||||
"sha256": "fb302b2902fea6a815a8266811f350d1e4c108ca88fafd49e6eddcbfff90a5e5",
|
||||
"size": 4268
|
||||
},
|
||||
{
|
||||
"path": "terminal/xterm-LICENSE.txt",
|
||||
@@ -124,13 +122,15 @@
|
||||
},
|
||||
{
|
||||
"path": "tools/StayHere.exe",
|
||||
"sha256": "ddbd6664360c6538c215abddb8c85a3694ca7976af2ecba3d9974d33c5c55187",
|
||||
"size": 133120
|
||||
"sha256": "bb595202cc4749f3c0fe47adc92d4b02a174abeb5765e493673de9f5e070251c",
|
||||
"size": 540583
|
||||
},
|
||||
{
|
||||
"path": "tools/StayHere-README.md",
|
||||
"sha256": "ba214e2aca7405e698b3ab21ceb1f41ce088edd996b3e30e4a9d58ff496f9413",
|
||||
"size": 2796
|
||||
}
|
||||
]
|
||||
],
|
||||
"version": "0.6.88",
|
||||
"schema": 1
|
||||
}
|
||||
+3
-3
@@ -9,8 +9,8 @@ IDC_SCREENSHOT_PENCIL CURSOR "icons/lucide/pencil.cur"
|
||||
1 RT_MANIFEST "app.manifest"
|
||||
201 RCDATA "update.ps1"
|
||||
1 VERSIONINFO
|
||||
FILEVERSION 0,6,86,0
|
||||
PRODUCTVERSION 0,6,86,0
|
||||
FILEVERSION 0,6,88,0
|
||||
PRODUCTVERSION 0,6,88,0
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
BEGIN
|
||||
@@ -20,7 +20,7 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "FileDescription", "KSwitcher — native keyboard and clipboard utility"
|
||||
VALUE "ProductName", "KSwitcher"
|
||||
VALUE "FileVersion", "0.6.86"
|
||||
VALUE "FileVersion", "0.6.88"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -58,6 +58,9 @@ terminal.attachCustomKeyEventHandler(event => {
|
||||
else if (event.code === 'KeyF' || event.code === 'KeyC') {
|
||||
if (!event.repeat) bridge.postMessage(event.code === 'KeyF' ? 'insert-path' : 'copy-command-line');
|
||||
}
|
||||
else if (event.key === 'Enter') {
|
||||
if (!event.repeat) bridge.postMessage('insert-name');
|
||||
}
|
||||
else return true;
|
||||
event.preventDefault();
|
||||
return false;
|
||||
|
||||
+16
-5
@@ -4,17 +4,25 @@
|
||||
#include <cstring>
|
||||
namespace ks
|
||||
{
|
||||
bool configurationPeer(HWND window, const wchar_t *filename)
|
||||
namespace
|
||||
{
|
||||
std::filesystem::path processImage(HWND window)
|
||||
{
|
||||
DWORD pid{};
|
||||
GetWindowThreadProcessId(window, &pid);
|
||||
HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
|
||||
if (!process) return false;
|
||||
if (!process) return {};
|
||||
wchar_t path[32768]; DWORD count = std::size(path);
|
||||
bool ok = QueryFullProcessImageNameW(process, 0, path, &count) != 0;
|
||||
CloseHandle(process);
|
||||
auto expected = std::filesystem::path(executable()).parent_path() / filename;
|
||||
return ok && _wcsicmp(path, expected.c_str()) == 0;
|
||||
return ok ? std::filesystem::path(path) : std::filesystem::path{};
|
||||
}
|
||||
}
|
||||
|
||||
bool configurationPeer(HWND window, const wchar_t *filename)
|
||||
{
|
||||
const auto expected = std::filesystem::path(executable()).parent_path() / filename;
|
||||
return _wcsicmp(processImage(window).c_str(), expected.c_str()) == 0;
|
||||
}
|
||||
void launchConfiguration(App &app, bool snippets, bool logs)
|
||||
{
|
||||
@@ -35,7 +43,10 @@ void launchConfiguration(App &app, bool snippets, bool logs)
|
||||
LRESULT configurationRequest(App &app, HWND sender, const COPYDATASTRUCT &message)
|
||||
{
|
||||
const bool filesPeers = message.dwData == FilesLanPeers;
|
||||
if (!configurationPeer(sender, filesPeers ? L"KSwitcher.Files.exe" : L"KSwitcher.Config.exe") || message.cbData > ConfigLimit ||
|
||||
const bool knownSender = filesPeers
|
||||
? isFilesPeerImage(processImage(sender), std::filesystem::path(executable()).parent_path())
|
||||
: configurationPeer(sender, L"KSwitcher.Config.exe");
|
||||
if (!knownSender || message.cbData > ConfigLimit ||
|
||||
(message.cbData && !message.lpData)) return FALSE;
|
||||
Bytes reply;
|
||||
bool ok = false;
|
||||
|
||||
@@ -1,6 +1,30 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include <filesystem>
|
||||
#include <string_view>
|
||||
|
||||
inline constexpr ULONG_PTR FilesLanPeers = 0x4b53010d;
|
||||
inline constexpr ULONG_PTR FilesLanPeersReply = 0x4b53010e;
|
||||
inline constexpr DWORD FilesLanPeersLimit = 64 * 1024;
|
||||
|
||||
namespace ks
|
||||
{
|
||||
inline bool isFilesPeerImage(const std::filesystem::path &image, const std::filesystem::path &install)
|
||||
{
|
||||
if (_wcsicmp(image.filename().c_str(), L"KSwitcher.Files.exe") != 0) return false;
|
||||
const auto directory = image.parent_path();
|
||||
if (_wcsicmp(directory.c_str(), install.c_str()) == 0) return true;
|
||||
|
||||
// Updating moves a still-running Files executable into the transaction backup.
|
||||
// Windows then reports the moved path for its process image.
|
||||
if (_wcsicmp(directory.filename().c_str(), L"backup") != 0) return false;
|
||||
const auto transaction = directory.parent_path();
|
||||
if (_wcsicmp(transaction.parent_path().c_str(), install.c_str()) != 0) return false;
|
||||
constexpr std::wstring_view prefix = L".kswitcher-update-";
|
||||
const auto name = transaction.filename().wstring();
|
||||
if (name.size() != prefix.size() + 32 || _wcsnicmp(name.c_str(), prefix.data(), prefix.size()) != 0)
|
||||
return false;
|
||||
return std::wstring_view(name).substr(prefix.size()).find_first_not_of(L"0123456789abcdefABCDEF")
|
||||
== std::wstring_view::npos;
|
||||
}
|
||||
}
|
||||
+7
-3
@@ -909,12 +909,14 @@ std::wstring powerShellPath(const fs::path &path)
|
||||
return quoted+L"'";
|
||||
}
|
||||
|
||||
void insertCurrentPanelPath()
|
||||
void insertCurrentPanelPath(bool nameOnly = false)
|
||||
{
|
||||
const auto &panel=panels[active];
|
||||
const auto row=currentPanelRow(panel);
|
||||
if(row==LB_ERR) return;
|
||||
const auto &path=panel.rows[row].native();
|
||||
const auto &item=panel.rows[row];
|
||||
if(nameOnly && row==0 && panel.directory.has_relative_path()) return;
|
||||
const auto path=nameOnly?item.filename().native():item.native();
|
||||
std::wstring quoted;
|
||||
if(path.find(L' ')!=std::wstring::npos)
|
||||
{
|
||||
@@ -974,6 +976,7 @@ bool ensureTerminalView(HWND owner)
|
||||
if(GetForegroundWindow()==owner) SetFocus(terminalInput);
|
||||
}
|
||||
else if(message==L"insert-path") insertCurrentPanelPath();
|
||||
else if(message==L"insert-name") insertCurrentPanelPath(true);
|
||||
else if(message==L"copy-command-line") SendMessageW(terminalInput,WM_COPY,0,0);
|
||||
else if(message==L"toggle") showTerminal(owner,!terminalVisible);
|
||||
else if(!terminalVisible && (message==L"grow" || message==L"shrink"))
|
||||
@@ -5147,11 +5150,12 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE, PWSTR, int show)
|
||||
{
|
||||
const bool ctrl=(GetKeyState(VK_CONTROL)&0x8000)!=0;
|
||||
const bool shift=(GetKeyState(VK_SHIFT)&0x8000)!=0;
|
||||
if(ctrl && !shift && (message.wParam=='F' || message.wParam=='C'))
|
||||
if(ctrl && !shift && (message.wParam=='F' || message.wParam=='C' || message.wParam==VK_RETURN))
|
||||
{
|
||||
if(!(message.lParam & (1LL<<30)))
|
||||
{
|
||||
if(message.wParam=='F') insertCurrentPanelPath();
|
||||
else if(message.wParam==VK_RETURN) insertCurrentPanelPath(true);
|
||||
else SendMessageW(terminalInput,WM_COPY,0,0);
|
||||
}
|
||||
continue;
|
||||
|
||||
+7
-2
@@ -247,7 +247,8 @@ struct RemoteCommand
|
||||
{
|
||||
if(process.value!=INVALID_HANDLE_VALUE) return ERROR_BUSY;
|
||||
if(command.empty() || command.size()>12000 || command.find(L'\0')!=command.npos) return ERROR_INVALID_PARAMETER;
|
||||
const auto script=L"[Console]::OutputEncoding=[Text.UTF8Encoding]::new($false); "
|
||||
const auto script=L"$ProgressPreference='SilentlyContinue'; "
|
||||
L"[Console]::OutputEncoding=[Text.UTF8Encoding]::new($false); "
|
||||
L"$OutputEncoding=[Console]::OutputEncoding;\r\n"+command;
|
||||
const auto data=reinterpret_cast<const BYTE *>(script.data());
|
||||
const auto bytes=static_cast<DWORD>(script.size()*sizeof(wchar_t));
|
||||
@@ -257,7 +258,11 @@ struct RemoteCommand
|
||||
std::wstring encoded(length,L'\0');
|
||||
if(!CryptBinaryToStringW(data,bytes,encoding,encoded.data(),&length)) return GetLastError();
|
||||
encoded.resize(length);
|
||||
auto arguments=L"powershell.exe -NoLogo -NoProfile -NonInteractive -OutputFormat Text -EncodedCommand "+encoded;
|
||||
// Windows PowerShell serializes its error stream as CLIXML with -EncodedCommand.
|
||||
// Decode the command inside a fixed text-mode expression instead.
|
||||
auto arguments=L"powershell.exe -NoLogo -NoProfile -NonInteractive -OutputFormat Text -Command "
|
||||
L"\"& ([ScriptBlock]::Create([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('"+
|
||||
encoded+L"'))))\"";
|
||||
wchar_t system[MAX_PATH]{};
|
||||
if(!GetSystemDirectoryW(system,MAX_PATH)) return GetLastError();
|
||||
const auto shell=std::wstring(system)+L"\\WindowsPowerShell\\v1.0\\powershell.exe";
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
#include "platform.h"
|
||||
namespace ks
|
||||
{
|
||||
inline constexpr wchar_t AppVersion[] = L"0.6.86";
|
||||
inline constexpr wchar_t AppVersion[] = L"0.6.88";
|
||||
inline constexpr UINT UpdateMessage = WM_APP + 7;
|
||||
inline constexpr UINT UpdateCheckIntervalMs = 6 * 60 * 60 * 1000;
|
||||
inline constexpr UINT UpdateNotificationRetryMs = 5 * 60 * 1000;
|
||||
|
||||
@@ -20,6 +20,9 @@ public static class FilesActivationUi {
|
||||
[DllImport("user32.dll")] public static extern IntPtr GetDlgItem(IntPtr window, int id);
|
||||
[DllImport("user32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr FindWindowEx(IntPtr parent, IntPtr after, string type, string title);
|
||||
[DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr window);
|
||||
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
|
||||
[DllImport("user32.dll")] static extern bool SetForegroundWindow(IntPtr window);
|
||||
[DllImport("user32.dll")] static extern void keybd_event(byte key, byte scan, uint flags, UIntPtr extra);
|
||||
[DllImport("user32.dll")] public static extern bool PostMessage(IntPtr window, uint msg, IntPtr w, IntPtr l);
|
||||
[DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr window, IntPtr process);
|
||||
[DllImport("user32.dll")] static extern bool GetGUIThreadInfo(uint thread, ref GuiInfo info);
|
||||
@@ -36,6 +39,14 @@ public static class FilesActivationUi {
|
||||
return info.Focus;
|
||||
}
|
||||
|
||||
public static bool Activate(IntPtr window) {
|
||||
if (GetForegroundWindow() == window) return true;
|
||||
keybd_event(0x12, 0, 0, UIntPtr.Zero);
|
||||
keybd_event(0x12, 0, 2, UIntPtr.Zero);
|
||||
SetForegroundWindow(window);
|
||||
return GetForegroundWindow() == window;
|
||||
}
|
||||
|
||||
public static void ClickRow(IntPtr list, int row, bool doubleClick) {
|
||||
Rect rect;
|
||||
if (SendRect(list, 0x198, new IntPtr(row), out rect).ToInt32() == -1)
|
||||
@@ -179,6 +190,21 @@ try {
|
||||
[FilesActivationUi]::Send($left, 0x188, [IntPtr]::Zero, [IntPtr]::Zero).ToInt32() -eq (Find-Row $left '..')
|
||||
}
|
||||
if ((Read-Text $inputBox) -cne 'draft command') { throw 'Pane navigation changed the command line.' }
|
||||
$shell = New-Object -ComObject WScript.Shell
|
||||
$fileName = [IO.Path]::GetFileName($document)
|
||||
foreach ($name in $fileName, 'Test folder') {
|
||||
$label = if ($name -eq $fileName) { $name } else { "[$name]" }
|
||||
Select-Row $right $label
|
||||
[void][FilesActivationUi]::Write($inputBox, 0x0C, [IntPtr]::Zero, 'draft command')
|
||||
[void][FilesActivationUi]::Send($inputBox, 0xB1, [IntPtr]6, [IntPtr]13)
|
||||
Wait-Until 'Files activation for Ctrl+Enter' {
|
||||
[FilesActivationUi]::Activate($window) -and
|
||||
[FilesActivationUi]::Focus($window) -eq $inputBox
|
||||
}
|
||||
$shell.SendKeys('^{ENTER}')
|
||||
$expected = 'draft "{0}"' -f $name
|
||||
Wait-Until "Ctrl+Enter inserting $name" { (Read-Text $inputBox) -ceq $expected }
|
||||
}
|
||||
[void][FilesActivationUi]::Write($inputBox, 0x0C, [IntPtr]::Zero, '')
|
||||
Write-Output 'Tab and Down navigated the panes while preserving command-line focus and text.'
|
||||
|
||||
|
||||
+28
-1
@@ -1,6 +1,7 @@
|
||||
#include <winsock2.h>
|
||||
#include <sddl.h>
|
||||
#include "lan_files.h"
|
||||
#include "files_lan_ipc.h"
|
||||
#include <bit>
|
||||
#include <atomic>
|
||||
#include <fstream>
|
||||
@@ -18,6 +19,23 @@ void require(bool condition,const char *message)
|
||||
if(!condition) throw std::runtime_error(message);
|
||||
}
|
||||
|
||||
void verifyFilesPeerImage()
|
||||
{
|
||||
const fs::path install = LR"(C:\Tools\KSwitcher)";
|
||||
const auto transaction = install / L".kswitcher-update-0123456789abcdef0123456789abcdef";
|
||||
constexpr auto files = L"KSwitcher.Files.exe";
|
||||
require(isFilesPeerImage(install / files, install), "installed Files can request LAN peers");
|
||||
require(isFilesPeerImage(transaction / L"backup" / files, install),
|
||||
"Files open during an update can still request LAN peers");
|
||||
require(!isFilesPeerImage(transaction / L"stage" / files, install), "staged Files image is rejected");
|
||||
require(!isFilesPeerImage(transaction / L"backup" / L"KSwitcher.Config.exe", install),
|
||||
"another executable in the backup is rejected");
|
||||
require(!isFilesPeerImage(install / L".kswitcher-update-invalid" / L"backup" / files, install),
|
||||
"malformed transaction directory is rejected");
|
||||
require(!isFilesPeerImage(install.parent_path() / transaction.filename() / L"backup" / files, install),
|
||||
"backup outside the active installation is rejected");
|
||||
}
|
||||
|
||||
void save(const fs::path &path,const std::string &text)
|
||||
{
|
||||
std::ofstream file(path,std::ios::binary|std::ios::trunc);
|
||||
@@ -111,6 +129,7 @@ int main()
|
||||
int result=0;
|
||||
try
|
||||
{
|
||||
verifyFilesPeerImage();
|
||||
fs::create_directory(root);
|
||||
const auto client=randomHex(16),first=randomHex(16),second=randomHex(16),key=randomHex(32);
|
||||
auto lookup=[&](const std::string &id){return id==client?key:std::string{};};
|
||||
@@ -140,10 +159,18 @@ int main()
|
||||
std::string console;
|
||||
const auto exit=runRemoteCommand(host,root,
|
||||
L"[Console]::Out.WriteLine((Get-Location).ProviderPath); "
|
||||
L"[Console]::Error.WriteLine('Тест stderr'); exit 7",
|
||||
L"[Console]::Error.WriteLine('Тест stderr'); Write-Error 'PowerShell error'; exit 7",
|
||||
[&](std::string_view part){console+=part;});
|
||||
require(exit==7 && console.find(utf8(root.native()))!=console.npos &&
|
||||
console.find(utf8(L"Тест stderr"))!=console.npos,"remote command output or exit code differs");
|
||||
require(console.find("PowerShell error")!=console.npos && console.find("CLIXML")==console.npos,
|
||||
"remote PowerShell errors were not returned as text");
|
||||
console.clear();
|
||||
const auto directoryExit=runRemoteCommand(host,root,
|
||||
L"Write-Progress -Activity 'Hidden progress probe' -Status 'Working'; dir",
|
||||
[&](std::string_view part){console+=part;});
|
||||
require(directoryExit==0 && console.find("CLIXML")==console.npos &&
|
||||
console.find("Hidden progress probe")==console.npos,"remote directory command included progress metadata");
|
||||
std::stop_source cancel;
|
||||
try
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user