mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-11-21 11:31:51 -05:00
Wrap QProcess to avoid inherited handles causing issues on Windows
This commit is contained in:
parent
86c76be111
commit
1a6474976c
@ -236,6 +236,7 @@ set (wsjt_qt_CXXSRCS
|
|||||||
WFPalette.cpp
|
WFPalette.cpp
|
||||||
Radio.cpp
|
Radio.cpp
|
||||||
RadioMetaType.cpp
|
RadioMetaType.cpp
|
||||||
|
NonInheritingProcess.cpp
|
||||||
models/IARURegions.cpp
|
models/IARURegions.cpp
|
||||||
models/Bands.cpp
|
models/Bands.cpp
|
||||||
models/Modes.cpp
|
models/Modes.cpp
|
||||||
|
114
NonInheritingProcess.cpp
Normal file
114
NonInheritingProcess.cpp
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
#include "NonInheritingProcess.hpp"
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif
|
||||||
|
#ifndef _UNICODE
|
||||||
|
#define _UNICODE
|
||||||
|
#endif
|
||||||
|
#ifdef _WIN32_WINNT
|
||||||
|
#undef _WIN32_WINNT
|
||||||
|
#endif
|
||||||
|
#define _WIN32_WINNT 0x0601
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "pimpl_impl.hpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct start_info_deleter
|
||||||
|
{
|
||||||
|
void operator () (STARTUPINFOEXW * si)
|
||||||
|
{
|
||||||
|
if (si->lpAttributeList)
|
||||||
|
{
|
||||||
|
::DeleteProcThreadAttributeList (si->lpAttributeList);
|
||||||
|
}
|
||||||
|
delete si;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class NonInheritingProcess::impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
void extend_CreateProcessArguments (QProcess::CreateProcessArguments * args)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Here we modify the CreateProcessArguments structure to use a
|
||||||
|
// STARTUPINFOEX extended argument to CreateProcess. In that we
|
||||||
|
// set up a list of handles for the new process to inherit. By
|
||||||
|
// doing this we stop all inherited handles from being
|
||||||
|
// inherited. Unfortunately UpdateProcThreadAttribute does not let
|
||||||
|
// us set up an empty handle list, so we populate the list with
|
||||||
|
// the three standard stream handles that QProcess::start has set
|
||||||
|
// up as Pipes to do IPC. Even though these Pipe handles are
|
||||||
|
// created with inheritance disabled, UpdateProcThreadAtribute and
|
||||||
|
// CreateProcess don't seem to mind, which suits us fine.
|
||||||
|
//
|
||||||
|
// Note: that we cannot just clear the inheritHandles flag as that
|
||||||
|
// stops the standard stream handles being inherited which breaks
|
||||||
|
// our IPC using std(in|out|err). Only be using a
|
||||||
|
// PROC_THREAD_ATTRIBUTE_HANDLE_LIST attribute in a STARTUPINFOEX
|
||||||
|
// structure can we avoid the all or nothing behaviour of
|
||||||
|
// CreateProcess /w respect to handle inheritance.
|
||||||
|
//
|
||||||
|
BOOL fSuccess;
|
||||||
|
SIZE_T size {0};
|
||||||
|
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = nullptr;
|
||||||
|
::InitializeProcThreadAttributeList (nullptr, 1, 0, &size);
|
||||||
|
lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST> (::HeapAlloc (::GetProcessHeap (), 0, size));
|
||||||
|
fSuccess = !!lpAttributeList;
|
||||||
|
if (fSuccess)
|
||||||
|
{
|
||||||
|
fSuccess = ::InitializeProcThreadAttributeList (lpAttributeList, 1, 0, &size);
|
||||||
|
}
|
||||||
|
if (fSuccess)
|
||||||
|
{
|
||||||
|
// empty list of handles
|
||||||
|
fSuccess = ::UpdateProcThreadAttribute (lpAttributeList, 0,
|
||||||
|
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
|
||||||
|
&args->startupInfo->hStdInput, 3 * sizeof (HANDLE),
|
||||||
|
nullptr, 0);
|
||||||
|
}
|
||||||
|
if (fSuccess)
|
||||||
|
{
|
||||||
|
start_info_.reset (new STARTUPINFOEXW);
|
||||||
|
start_info_->StartupInfo = *args->startupInfo;
|
||||||
|
start_info_->StartupInfo.cb = sizeof (STARTUPINFOEXW);
|
||||||
|
start_info_->lpAttributeList = lpAttributeList;
|
||||||
|
args->startupInfo = reinterpret_cast<Q_STARTUPINFO*> (start_info_.get ());
|
||||||
|
args->flags |= EXTENDED_STARTUPINFO_PRESENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using start_info_type = std::unique_ptr<STARTUPINFOEXW, start_info_deleter>;
|
||||||
|
start_info_type start_info_;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
NonInheritingProcess::NonInheritingProcess (QObject * parent)
|
||||||
|
: QProcess {parent}
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
using namespace std::placeholders;
|
||||||
|
|
||||||
|
// enable cleanup after process starts or fails to start
|
||||||
|
connect (this, &QProcess::started, [this] {m_->start_info_.reset ();});
|
||||||
|
connect (this, &QProcess::errorOccurred, [this] (QProcess::ProcessError) {m_->start_info_.reset ();});
|
||||||
|
setCreateProcessArgumentsModifier (std::bind (&NonInheritingProcess::impl::extend_CreateProcessArguments, &*m_, _1));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
NonInheritingProcess::~NonInheritingProcess ()
|
||||||
|
{
|
||||||
|
}
|
35
NonInheritingProcess.hpp
Normal file
35
NonInheritingProcess.hpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef NON_INHERITING_PROCESS_HPP__
|
||||||
|
#define NON_INHERITING_PROCESS_HPP__
|
||||||
|
|
||||||
|
#include <QProcess>
|
||||||
|
#include "pimpl_h.hpp"
|
||||||
|
|
||||||
|
class QObject;
|
||||||
|
|
||||||
|
//
|
||||||
|
// class NonInheritingProcess - Manage a process without it inheriting
|
||||||
|
// all inheritable handles
|
||||||
|
//
|
||||||
|
// On MS Windows QProcess creates sub-processes which inherit all
|
||||||
|
// inheritable handles, and handles on Windows are inheritable by
|
||||||
|
// default. This can cause the lifetime of objects to be unexpectedly
|
||||||
|
// extended, which in turn can cause unexpected errors. The motivation
|
||||||
|
// for this class was implementing log file rotation using the Boost
|
||||||
|
// log library. The current log file's handle gets inherited by any
|
||||||
|
// long running sub-process started by QProcess and that causes a
|
||||||
|
// sharing violation when attempting to rename the log file on
|
||||||
|
// rotation, even though the log library closes the current log file
|
||||||
|
// before trying to rename it.
|
||||||
|
//
|
||||||
|
class NonInheritingProcess
|
||||||
|
: public QProcess
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NonInheritingProcess (QObject * parent = nullptr);
|
||||||
|
~NonInheritingProcess ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
class impl;
|
||||||
|
pimpl<impl> m_;
|
||||||
|
};
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user