mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-09 12:46:47 +02:00
Fix bootstrapper getting stuck on "installing dotnet" (#5565)
This commit is contained in:
@@ -774,19 +774,25 @@ bool find_app_name_in_path(const std::wstring& where, const std::vector<std::wst
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> exec_and_read_output(const std::wstring_view command, const DWORD timeout)
|
std::optional<std::string> exec_and_read_output(const std::wstring_view command, DWORD timeout_ms)
|
||||||
{
|
{
|
||||||
SECURITY_ATTRIBUTES saAttr{ sizeof(saAttr) };
|
SECURITY_ATTRIBUTES saAttr{ sizeof(saAttr) };
|
||||||
saAttr.bInheritHandle = true;
|
saAttr.bInheritHandle = false;
|
||||||
|
|
||||||
wil::unique_handle childStdoutRead;
|
constexpr size_t bufferSize = 4096;
|
||||||
wil::unique_handle childStdoutWrite;
|
// We must use a named pipe for async I/O
|
||||||
if (!CreatePipe(&childStdoutRead, &childStdoutWrite, &saAttr, 0))
|
char pipename[MAX_PATH + 1];
|
||||||
|
if (!GetTempFileNameA(R"(\\.\pipe\)", "tmp", 1, pipename))
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SetHandleInformation(childStdoutRead.get(), HANDLE_FLAG_INHERIT, 0))
|
wil::unique_handle readPipe{ CreateNamedPipeA(pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, PIPE_UNLIMITED_INSTANCES, bufferSize, bufferSize, 0, &saAttr) };
|
||||||
|
|
||||||
|
saAttr.bInheritHandle = true;
|
||||||
|
wil::unique_handle writePipe{ CreateFileA(pipename, GENERIC_WRITE, 0, &saAttr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr) };
|
||||||
|
|
||||||
|
if (!readPipe || !writePipe)
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -794,8 +800,8 @@ std::optional<std::string> exec_and_read_output(const std::wstring_view command,
|
|||||||
PROCESS_INFORMATION piProcInfo{};
|
PROCESS_INFORMATION piProcInfo{};
|
||||||
STARTUPINFOW siStartInfo{ sizeof(siStartInfo) };
|
STARTUPINFOW siStartInfo{ sizeof(siStartInfo) };
|
||||||
|
|
||||||
siStartInfo.hStdError = childStdoutWrite.get();
|
siStartInfo.hStdError = writePipe.get();
|
||||||
siStartInfo.hStdOutput = childStdoutWrite.get();
|
siStartInfo.hStdOutput = writePipe.get();
|
||||||
siStartInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
siStartInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
||||||
siStartInfo.wShowWindow = SW_HIDE;
|
siStartInfo.wShowWindow = SW_HIDE;
|
||||||
|
|
||||||
@@ -813,24 +819,49 @@ std::optional<std::string> exec_and_read_output(const std::wstring_view command,
|
|||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
// Child process inherited the write end of the pipe, we can close it now
|
||||||
|
writePipe.reset();
|
||||||
|
|
||||||
WaitForSingleObject(piProcInfo.hProcess, timeout);
|
auto closeProcessHandles = wil::scope_exit([&] {
|
||||||
|
CloseHandle(piProcInfo.hThread);
|
||||||
childStdoutWrite.reset();
|
CloseHandle(piProcInfo.hProcess);
|
||||||
CloseHandle(piProcInfo.hThread);
|
});
|
||||||
|
|
||||||
std::string childOutput;
|
std::string childOutput;
|
||||||
|
bool processExited = false;
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
char buffer[4096];
|
char buffer[bufferSize];
|
||||||
DWORD gotBytes = 0;
|
DWORD gotBytes = 0;
|
||||||
if (!ReadFile(childStdoutRead.get(), buffer, sizeof(buffer), &gotBytes, nullptr) || !gotBytes)
|
wil::unique_handle IOEvent{ CreateEventW(nullptr, true, false, nullptr) };
|
||||||
{
|
OVERLAPPED overlapped{ .hEvent = IOEvent.get() };
|
||||||
break;
|
ReadFile(readPipe.get(), buffer, sizeof(buffer), nullptr, &overlapped);
|
||||||
}
|
|
||||||
childOutput += std::string_view{ buffer, gotBytes };
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseHandle(piProcInfo.hProcess);
|
const std::array<HANDLE, 2> handlesToWait = { overlapped.hEvent, piProcInfo.hProcess };
|
||||||
|
switch (WaitForMultipleObjects(1 + !processExited, handlesToWait.data(), false, timeout_ms))
|
||||||
|
{
|
||||||
|
case WAIT_OBJECT_0 + 1:
|
||||||
|
if (!processExited)
|
||||||
|
{
|
||||||
|
// When the process exits, we can reduce timeout and read the rest of the output w/o possibly big timeout
|
||||||
|
timeout_ms = 1000;
|
||||||
|
processExited = true;
|
||||||
|
closeProcessHandles.reset();
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
case WAIT_OBJECT_0:
|
||||||
|
if (GetOverlappedResultEx(readPipe.get(), &overlapped, &gotBytes, timeout_ms, true))
|
||||||
|
{
|
||||||
|
childOutput += std::string_view{ buffer, gotBytes };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Timeout
|
||||||
|
[[fallthrough]];
|
||||||
|
default:
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit:
|
||||||
|
CancelIo(readPipe.get());
|
||||||
return childOutput;
|
return childOutput;
|
||||||
}
|
}
|
||||||
@@ -99,7 +99,7 @@ std::wstring get_resource_string(UINT resource_id, HINSTANCE instance, const wch
|
|||||||
// is added to the .cpp file.
|
// is added to the .cpp file.
|
||||||
#define GET_RESOURCE_STRING(resource_id) get_resource_string(resource_id, reinterpret_cast<HINSTANCE>(&__ImageBase), L#resource_id)
|
#define GET_RESOURCE_STRING(resource_id) get_resource_string(resource_id, reinterpret_cast<HINSTANCE>(&__ImageBase), L#resource_id)
|
||||||
|
|
||||||
std::optional<std::string> exec_and_read_output(const std::wstring_view command, const DWORD timeout = INFINITE);
|
std::optional<std::string> exec_and_read_output(const std::wstring_view command, DWORD timeout_ms = 30000);
|
||||||
|
|
||||||
// Helper class for various COM-related APIs, e.g working with security descriptors
|
// Helper class for various COM-related APIs, e.g working with security descriptors
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|||||||
Reference in New Issue
Block a user