Cleanup where.exe invocation (#958)

This commit is contained in:
Pavel Krymets 2018-06-22 07:36:46 -07:00 committed by GitHub
parent da206e8afe
commit b12c9b33f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 131 additions and 86 deletions

View File

@ -197,6 +197,7 @@
<ClInclude Include="exceptions.h" />
<ClInclude Include="GlobalVersionUtility.h" />
<ClInclude Include="fx_ver.h" />
<ClInclude Include="HandleWrapper.h" />
<ClInclude Include="hostfxroptions.h" />
<ClInclude Include="hostfxr_utility.h" />
<ClInclude Include="iapplication.h" />

View File

@ -0,0 +1,52 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#pragma once
#include <Windows.h>
struct InvalidHandleTraits
{
using HandleType = HANDLE;
static const HANDLE DefaultHandle;
static void Close(HANDLE handle) { CloseHandle(handle); }
};
// Workaround for VC++ bug https://developercommunity.visualstudio.com/content/problem/33928/constexpr-failing-on-nullptr-v141-compiler-regress.html
const HANDLE InvalidHandleTraits::DefaultHandle = INVALID_HANDLE_VALUE;
struct NullHandleTraits
{
using HandleType = HANDLE;
static constexpr HANDLE DefaultHandle = NULL;
static void Close(HANDLE handle) { CloseHandle(handle); }
};
template<typename traits>
class HandleWrapper
{
public:
using HandleType = typename traits::HandleType;
HandleWrapper(HandleType handle = traits::DefaultHandle) : m_handle(handle) { }
~HandleWrapper()
{
if (m_handle != traits::DefaultHandle)
{
traits::Close(m_handle);
}
}
operator HandleType() { return m_handle; }
HandleWrapper& operator =(HandleType value)
{
DBG_ASSERT(m_handle == traits::DefaultHandle);
m_handle = value;
return *this;
}
HandleType* operator&() { return &m_handle; }
private:
HandleType m_handle;
};

View File

@ -26,12 +26,21 @@
#endif
#define OBSERVE_CAUGHT_EXCEPTION() CaughtExceptionHResult(LOCATION_INFO);
#define RETURN_CAUGHT_EXCEPTION() return CaughtExceptionHResult(LOCATION_INFO);
#define CATCH_RETURN() catch (...) { RETURN_CAUGHT_EXCEPTION(); }
#define THROW_IF_NULL_ALLOC(ptr) Throw_IfNullAlloc(ptr)
#define RETURN_IF_FAILED(hr) do { HRESULT __hrRet = hr; if (FAILED(__hrRet)) { LogHResultFailed(LOCATION_INFO, __hrRet); return __hrRet; }} while (0, 0)
#define RETURN_CAUGHT_EXCEPTION() return CaughtExceptionHResult(LOCATION_INFO);
#define RETURN_LAST_ERROR_IF(condition) do { if (condition) { return LogLastError(LOCATION_INFO); }} while (0, 0)
#define RETURN_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { return LogLastError(LOCATION_INFO); }} while (0, 0)
#define FINISHED_IF_FAILED(hrr) do { HRESULT __hrRet = hrr; if (FAILED(__hrRet)) { LogHResultFailed(LOCATION_INFO, __hrRet); hr = __hrRet; goto Finished; }} while (0, 0)
#define FINISHED_IF_NULL_ALLOC(ptr) do { if ((ptr) == nullptr) { hr = LogHResultFailed(LOCATION_INFO, E_OUTOFMEMORY); goto Finished; }} while (0, 0)
#define FINISHED_LAST_ERROR_IF(condition) do { if (condition) { hr = LogLastError(LOCATION_INFO); goto Finished; }} while (0, 0)
#define FINISHED_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { hr = LogLastError(LOCATION_INFO); goto Finished; }} while (0, 0)
#define THROW_IF_NULL_ALLOC(ptr) Throw_IfNullAlloc(ptr)
#define CATCH_RETURN() catch (...) { RETURN_CAUGHT_EXCEPTION(); }
#define LOG_IF_FAILED(hr) LogHResultFailed(LOCATION_INFO, hr)
#define LOG_LAST_ERROR_IF(condition) LogLastErrorIf(LOCATION_INFO, condition)
#define SUCCEEDED_LOG(hr) SUCCEEDED(LOG_IF_FAILED(hr))
#define FAILED_LOG(hr) FAILED(LOG_IF_FAILED(hr))
@ -40,6 +49,26 @@
DebugPrintf(ASPNETCORE_DEBUG_FLAG_ERROR, LOCATION_FORMAT "Unhandled non-standard exception", LOCATION_CALL_ONLY);
}
__declspec(noinline) inline HRESULT LogLastError(LOCATION_ARGUMENTS_ONLY)
{
const auto lastError = GetLastError();
const auto hr = HRESULT_FROM_WIN32(lastError);
DebugPrintf(ASPNETCORE_DEBUG_FLAG_ERROR, LOCATION_FORMAT "Operation failed with LastError: %d HR: 0x%x", LOCATION_CALL lastError, hr);
return hr;
}
__declspec(noinline) inline bool LogLastErrorIf(LOCATION_ARGUMENTS_ONLY, bool condition)
{
if (condition)
{
LogLastError(LOCATION_CALL_ONLY);
}
return condition;
}
__declspec(noinline) inline VOID ReportException(LOCATION_ARGUMENTS std::exception& exception)
{
DebugPrintf(ASPNETCORE_DEBUG_FLAG_ERROR, LOCATION_FORMAT "Unhandled exception: %s", LOCATION_CALL exception.what());

View File

@ -4,11 +4,13 @@
#include "hostfxr_utility.h"
#include <string>
#include <atlcomcli.h>
#include "EventLog.h"
#include "ntassert.h"
#include "fx_ver.h"
#include "debugutil.h"
#include "exceptions.h"
#include "HandleWrapper.h"
namespace fs = std::experimental::filesystem;
@ -440,9 +442,11 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
SECURITY_ATTRIBUTES securityAttributes;
CHAR pzFileContents[READ_BUFFER_SIZE];
HANDLE hStdOutReadPipe = INVALID_HANDLE_VALUE;
HANDLE hStdOutWritePipe = INVALID_HANDLE_VALUE;
LPWSTR pwzDotnetName = NULL;
HandleWrapper<InvalidHandleTraits> hStdOutReadPipe;
HandleWrapper<InvalidHandleTraits> hStdOutWritePipe;
HandleWrapper<InvalidHandleTraits> hProcess;
HandleWrapper<InvalidHandleTraits> hThread;
CComBSTR pwzDotnetName = NULL;
DWORD dwFilePointer;
BOOL fIsWow64Process;
BOOL fIsCurrentProcess64Bit;
@ -453,8 +457,6 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
DWORD dwBinaryType;
INT index = 0;
INT prevIndex = 0;
BOOL fProcessCreationResult = FALSE;
BOOL fResult = FALSE;
std::optional<fs::path> result;
// Set the security attributes for the read/write pipe
@ -462,17 +464,11 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
securityAttributes.lpSecurityDescriptor = NULL;
securityAttributes.bInheritHandle = TRUE;
LOG_INFO("Invoking where.exe to find dotnet.exe");
// Create a read/write pipe that will be used for reading the result of where.exe
if (!CreatePipe(&hStdOutReadPipe, &hStdOutWritePipe, &securityAttributes, 0))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
if (!SetHandleInformation(hStdOutReadPipe, HANDLE_FLAG_INHERIT, 0))
{
hr = ERROR_FILE_INVALID;
goto Finished;
}
FINISHED_LAST_ERROR_IF(!CreatePipe(&hStdOutReadPipe, &hStdOutWritePipe, &securityAttributes, 0));
FINISHED_LAST_ERROR_IF(!SetHandleInformation(hStdOutReadPipe, HANDLE_FLAG_INHERIT, 0));
// Set the stdout and err pipe to the write pipes.
startupInfo.cb = sizeof(startupInfo);
@ -482,14 +478,10 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
// CreateProcess requires a mutable string to be passed to commandline
// See https://blogs.msdn.microsoft.com/oldnewthing/20090601-00/?p=18083/
pwzDotnetName = SysAllocString(L"\"where.exe\" dotnet.exe");
if (pwzDotnetName == NULL)
{
goto Finished;
}
pwzDotnetName = L"\"where.exe\" dotnet.exe";
// Create a process to invoke where.exe
fProcessCreationResult = CreateProcessW(NULL,
FINISHED_LAST_ERROR_IF(!CreateProcessW(NULL,
pwzDotnetName,
NULL,
NULL,
@ -499,20 +491,18 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
NULL,
&startupInfo,
&processInformation
);
));
if (!fProcessCreationResult)
{
goto Finished;
}
// Store handles into wrapper so they get closed automatically
hProcess = processInformation.hProcess;
hThread = processInformation.hThread;
// Wait for where.exe to return, waiting 2 seconds.
if (WaitForSingleObject(processInformation.hProcess, 2000) != WAIT_OBJECT_0)
if (LOG_LAST_ERROR_IF(WaitForSingleObject(processInformation.hProcess, 2000) != WAIT_OBJECT_0))
{
// Timeout occured, terminate the where.exe process and return.
TerminateProcess(processInformation.hProcess, 2);
hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
goto Finished;
FINISHED_LAST_ERROR_IF(!TerminateProcess(processInformation.hProcess, 2));
FINISHED_IF_FAILED(HRESULT_FROM_WIN32(ERROR_TIMEOUT));
}
//
@ -520,10 +510,7 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
// and 2 if there was an error. Check if the exit code is 1 and set
// a new hr result saying it couldn't find dotnet.exe
//
if (!GetExitCodeProcess(processInformation.hProcess, &dwExitCode))
{
goto Finished;
}
FINISHED_LAST_ERROR_IF (!GetExitCodeProcess(processInformation.hProcess, &dwExitCode));
//
// In this block, if anything fails, we will goto our fallback of
@ -531,7 +518,7 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
//
if (dwExitCode != 0)
{
goto Finished;
FINISHED_IF_FAILED(E_FAIL);
}
// Where succeeded.
@ -539,38 +526,30 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
dwFilePointer = SetFilePointer(hStdOutReadPipe, 0, NULL, FILE_BEGIN);
if (dwFilePointer == INVALID_SET_FILE_POINTER)
{
goto Finished;
FINISHED_IF_FAILED(E_FAIL);
}
//
// As the call to where.exe succeeded (dotnet.exe was found), ReadFile should not hang.
// TODO consider putting ReadFile in a separate thread with a timeout to guarantee it doesn't block.
//
if (!ReadFile(hStdOutReadPipe, pzFileContents, READ_BUFFER_SIZE, &dwNumBytesRead, NULL))
{
goto Finished;
}
FINISHED_LAST_ERROR_IF (!ReadFile(hStdOutReadPipe, pzFileContents, READ_BUFFER_SIZE, &dwNumBytesRead, NULL));
if (dwNumBytesRead >= READ_BUFFER_SIZE)
{
// This shouldn't ever be this large. We could continue to call ReadFile in a loop,
// however if someone had this many dotnet.exes on their machine.
goto Finished;
FINISHED_IF_FAILED(E_FAIL);
}
hr = HRESULT_FROM_WIN32(GetLastError());
if (FAILED(hr = struDotnetLocationsString.CopyA(pzFileContents, dwNumBytesRead)))
{
goto Finished;
}
FINISHED_IF_FAILED(struDotnetLocationsString.CopyA(pzFileContents, dwNumBytesRead));
WLOG_INFOF(L"where.exe invocation returned: %s", struDotnetLocationsString.QueryStr());
// Check the bitness of the currently running process
// matches the dotnet.exe found.
if (!IsWow64Process(GetCurrentProcess(), &fIsWow64Process))
{
// Calling IsWow64Process failed
goto Finished;
}
FINISHED_LAST_ERROR_IF (!IsWow64Process(GetCurrentProcess(), &fIsWow64Process));
if (fIsWow64Process)
{
// 32 bit mode
@ -584,6 +563,8 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
fIsCurrentProcess64Bit = systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
}
WLOG_INFOF(L"Current process bitness type detected as isX64=%d", fIsCurrentProcess64Bit);
while (TRUE)
{
index = struDotnetLocationsString.IndexOf(L"\r\n", prevIndex);
@ -591,46 +572,28 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
{
break;
}
if (FAILED(hr = struDotnetSubstring.Copy(&struDotnetLocationsString.QueryStr()[prevIndex], index - prevIndex)))
{
goto Finished;
}
FINISHED_IF_FAILED(struDotnetSubstring.Copy(&struDotnetLocationsString.QueryStr()[prevIndex], index - prevIndex));
// \r\n is two wchars, so add 2 here.
prevIndex = index + 2;
if (GetBinaryTypeW(struDotnetSubstring.QueryStr(), &dwBinaryType) &&
fIsCurrentProcess64Bit == (dwBinaryType == SCS_64BIT_BINARY))
WLOG_INFOF(L"Processing entry %s", struDotnetSubstring.QueryStr());
if (LOG_LAST_ERROR_IF(!GetBinaryTypeW(struDotnetSubstring.QueryStr(), &dwBinaryType)))
{
continue;
}
WLOG_INFOF(L"Binary type %d", dwBinaryType);
if (fIsCurrentProcess64Bit == (dwBinaryType == SCS_64BIT_BINARY))
{
// The bitness of dotnet matched with the current worker process bitness.
result = std::make_optional(struDotnetSubstring.QueryStr());
fResult = TRUE;
break;
return std::make_optional(struDotnetSubstring.QueryStr());
}
}
Finished:
if (hStdOutReadPipe != INVALID_HANDLE_VALUE)
{
CloseHandle(hStdOutReadPipe);
}
if (hStdOutWritePipe != INVALID_HANDLE_VALUE)
{
CloseHandle(hStdOutWritePipe);
}
if (processInformation.hProcess != INVALID_HANDLE_VALUE)
{
CloseHandle(processInformation.hProcess);
}
if (processInformation.hThread != INVALID_HANDLE_VALUE)
{
CloseHandle(processInformation.hThread);
}
if (pwzDotnetName != NULL)
{
SysFreeString(pwzDotnetName);
}
Finished:
return result;
}