Propagate spawn GetLastError() so it can checked; print nice error messages

This commit is contained in:
Ryan Prichard 2015-10-22 23:57:30 -05:00
parent 1b71ef07cc
commit 83c5e6de18
7 changed files with 87 additions and 9 deletions

View File

@ -39,7 +39,7 @@ RemoteWorker::RemoteWorker(const std::string &name) :
RemoteWorker::RemoteWorker(SpawnParams params) :
RemoteWorker(newWorkerName()) {
m_process = spawn(m_name, params);
m_process = spawn(m_name, params, nullptr);
ASSERT(m_process != nullptr && "Could not create RemoteWorker");
m_valid = true;
// Perform an RPC just to ensure that the worker process is ready, and
@ -53,12 +53,16 @@ RemoteWorker RemoteWorker::child(SpawnParams params) {
return ret;
}
RemoteWorker RemoteWorker::tryChild(SpawnParams params) {
RemoteWorker RemoteWorker::tryChild(SpawnParams params, DWORD *errCode) {
RemoteWorker ret(newWorkerName());
cmd().u.spawn.spawnName = ret.m_name;
cmd().u.spawn.spawnParams = params;
rpc(Command::SpawnChild);
if (cmd().handle != nullptr) {
if (cmd().handle == nullptr) {
if (errCode != nullptr) {
*errCode = cmd().dword;
}
} else {
BOOL dupSuccess = DuplicateHandle(
m_process,
cmd().handle,

View File

@ -21,7 +21,7 @@ public:
RemoteWorker() : RemoteWorker(SpawnParams { false, CREATE_NEW_CONSOLE }) {}
RemoteWorker(SpawnParams params);
RemoteWorker child(SpawnParams params={});
RemoteWorker tryChild(SpawnParams params={});
RemoteWorker tryChild(SpawnParams params={}, DWORD *errCode=nullptr);
~RemoteWorker() { cleanup(); }
bool valid() { return m_valid; }
void exit();

View File

@ -20,7 +20,9 @@ static std::vector<wchar_t> wstrToWVector(const std::wstring &str) {
} // anonymous namespace
HANDLE spawn(const std::string &workerName, const SpawnParams &params) {
HANDLE spawn(const std::string &workerName,
const SpawnParams &params,
DWORD *lastError) {
auto workerPath = pathDirName(getModuleFileName(NULL)) + "\\Worker.exe";
const std::wstring workerPathWStr = widenString(workerPath);
const std::string cmdLine = "\"" + workerPath + "\" " + workerName;
@ -80,7 +82,11 @@ HANDLE spawn(const std::string &workerName, const SpawnParams &params) {
inheritList.data(),
params.inheritCount * sizeof(HANDLE),
nullptr, nullptr);
ASSERT(success && "UpdateProcThreadAttribute failed");
if (!success) {
trace("Aborting: UpdateProcThreadAttribute failed: %s",
errorString(GetLastError()).c_str());
abort();
}
}
}
@ -95,7 +101,11 @@ HANDLE spawn(const std::string &workerName, const SpawnParams &params) {
NULL, NULL,
&suix.StartupInfo, &pi);
if (!success) {
trace("CreateProcessW failed: GetLastError=0x%x", GetLastError());
if (lastError != nullptr) {
*lastError = GetLastError();
}
trace("CreateProcessW failed: %s",
errorString(GetLastError()).c_str());
} else {
ret = pi.hProcess;
CloseHandle(pi.hThread);

View File

@ -19,4 +19,6 @@ struct SpawnParams {
}
};
HANDLE spawn(const std::string &workerName, const SpawnParams &params);
HANDLE spawn(const std::string &workerName,
const SpawnParams &params,
DWORD *lastError);

View File

@ -1,5 +1,7 @@
#include "Util.h"
#include <sstream>
#include "UnicodeConversions.h"
#include <WinptyAssert.h>
@ -22,3 +24,61 @@ std::string getModuleFileName(HMODULE module)
ASSERT(actual > 0 && actual < size);
return narrowString(filename);
}
// Convert GetLastError()'s error code to a presentable message such as:
//
// <87:The parameter is incorrect.>
//
std::string errorString(DWORD errCode) {
// MSDN has this note about "Windows 10":
//
// Windows 10:
//
// LocalFree is not in the modern SDK, so it cannot be used to free
// the result buffer. Instead, use HeapFree (GetProcessHeap(),
// allocatedMessage). In this case, this is the same as calling
// LocalFree on memory.
//
// Important: LocalAlloc() has different options: LMEM_FIXED, and
// LMEM_MOVABLE. FormatMessage() uses LMEM_FIXED, so HeapFree can be
// used. If LMEM_MOVABLE is used, HeapFree cannot be used.
//
// My interpretation of this note is:
// * "Windows 10" really just means, "the latest MS SDK", which supports
// Windows 10, as well as older releases.
// * In every NT kernel ever, HeapFree is perfectly fine to use with
// LocalAlloc LMEM_FIXED allocations.
// * In every NT kernel ever, the FormatMessage buffer can be freed with
// HeapFree.
// The note is clumsy, though. Without clarity, I can't safely use
// HeapFree, but apparently LocalFree calls stop compiling in the newest
// SDK.
//
// Instead, I'll use a fixed-size buffer.
std::stringstream ss;
ss << "<" << errCode << ":";
std::vector<wchar_t> msgBuf(1024);
DWORD ret = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
errCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
msgBuf.data(),
msgBuf.size(),
nullptr);
if (ret == 0) {
ss << "FormatMessageW failed:";
ss << GetLastError();
} else {
msgBuf[msgBuf.size() - 1] = L'\0';
std::string msg = narrowString(std::wstring(msgBuf.data()));
if (msg.size() >= 2 && msg.substr(msg.size() - 2) == "\r\n") {
msg.resize(msg.size() - 2);
}
ss << msg;
}
ss << ">";
return ss.str();
}

View File

@ -7,3 +7,4 @@
std::string pathDirName(const std::string &path);
std::string getModuleFileName(HMODULE module);
std::string errorString(DWORD errCode);

View File

@ -312,7 +312,8 @@ int main(int argc, char *argv[]) {
case Command::SpawnChild:
trace("Spawning child...");
cmd.handle = spawn(cmd.u.spawn.spawnName.str(),
cmd.u.spawn.spawnParams);
cmd.u.spawn.spawnParams,
&cmd.dword);
if (cmd.handle != nullptr) {
trace("Spawning child... pid %u",
(unsigned int)GetProcessId(cmd.handle));