Call AllocConsole (#1351)

This commit is contained in:
Justin Kotalik 2018-08-30 12:20:53 -07:00 committed by GitHub
parent e7f3c7c62e
commit 729a98adfc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 82 additions and 12 deletions

View File

@ -20,7 +20,8 @@ FileOutputManager::FileOutputManager(bool fEnableNativeLogging) :
m_disposed(false), m_disposed(false),
stdoutWrapper(nullptr), stdoutWrapper(nullptr),
stderrWrapper(nullptr), stderrWrapper(nullptr),
m_fEnableNativeRedirection(fEnableNativeLogging) m_fEnableNativeRedirection(fEnableNativeLogging),
m_fCreatedConsole(false)
{ {
InitializeSRWLock(&m_srwLock); InitializeSRWLock(&m_srwLock);
} }
@ -49,6 +50,21 @@ FileOutputManager::Start()
STRU struPath; STRU struPath;
FILETIME processCreationTime; FILETIME processCreationTime;
FILETIME dummyFileTime; FILETIME dummyFileTime;
// To make Console.* functions work, allocate a console
// in the current process.
if (!AllocConsole())
{
// ERROR_ACCESS_DENIED means there is a console already present.
if (GetLastError() != ERROR_ACCESS_DENIED)
{
RETURN_LAST_ERROR();
}
}
else
{
m_fCreatedConsole = true;
}
// Concatenate the log file name and application path // Concatenate the log file name and application path
RETURN_IF_FAILED(FILE_UTILITY::ConvertPathToFullPath( RETURN_IF_FAILED(FILE_UTILITY::ConvertPathToFullPath(
@ -129,6 +145,11 @@ FileOutputManager::Stop()
m_disposed = true; m_disposed = true;
if (m_fCreatedConsole)
{
RETURN_LAST_ERROR_IF(!FreeConsole());
}
if (m_hLogFileHandle == INVALID_HANDLE_VALUE) if (m_hLogFileHandle == INVALID_HANDLE_VALUE)
{ {
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);

View File

@ -35,6 +35,7 @@ private:
STRA m_straFileContent; STRA m_straFileContent;
BOOL m_disposed; BOOL m_disposed;
BOOL m_fEnableNativeRedirection; BOOL m_fEnableNativeRedirection;
BOOL m_fCreatedConsole;
SRWLOCK m_srwLock{}; SRWLOCK m_srwLock{};
std::unique_ptr<StdWrapper> stdoutWrapper; std::unique_ptr<StdWrapper> stdoutWrapper;
std::unique_ptr<StdWrapper> stderrWrapper; std::unique_ptr<StdWrapper> stderrWrapper;

View File

@ -27,13 +27,18 @@ LoggingHelpers::CreateLoggingProvider(
try try
{ {
// Check if there is an existing active console window before redirecting
// Window == IISExpress with active console window, don't redirect to a pipe
// if true.
CONSOLE_SCREEN_BUFFER_INFO dummy;
if (fIsLoggingEnabled) if (fIsLoggingEnabled)
{ {
auto manager = std::make_unique<FileOutputManager>(fEnableNativeLogging); auto manager = std::make_unique<FileOutputManager>(fEnableNativeLogging);
hr = manager->Initialize(pwzStdOutFileName, pwzApplicationPath); hr = manager->Initialize(pwzStdOutFileName, pwzApplicationPath);
outputManager = std::move(manager); outputManager = std::move(manager);
} }
else if (!GetConsoleWindow()) else if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &dummy))
{ {
outputManager = std::make_unique<PipeOutputManager>(fEnableNativeLogging); outputManager = std::make_unique<PipeOutputManager>(fEnableNativeLogging);
} }

View File

@ -24,7 +24,8 @@ PipeOutputManager::PipeOutputManager(bool fEnableNativeLogging) :
m_disposed(FALSE), m_disposed(FALSE),
m_fEnableNativeRedirection(fEnableNativeLogging), m_fEnableNativeRedirection(fEnableNativeLogging),
stdoutWrapper(nullptr), stdoutWrapper(nullptr),
stderrWrapper(nullptr) stderrWrapper(nullptr),
m_fCreatedConsole(false)
{ {
InitializeSRWLock(&m_srwLock); InitializeSRWLock(&m_srwLock);
} }
@ -43,6 +44,21 @@ HRESULT PipeOutputManager::Start()
HANDLE hStdErrReadPipe; HANDLE hStdErrReadPipe;
HANDLE hStdErrWritePipe; HANDLE hStdErrWritePipe;
// To make Console.* functions work, allocate a console
// in the current process.
if (!AllocConsole())
{
// ERROR_ACCESS_DENIED means there is a console already present.
if (GetLastError() != ERROR_ACCESS_DENIED)
{
RETURN_LAST_ERROR();
}
}
else
{
m_fCreatedConsole = true;
}
RETURN_LAST_ERROR_IF(!CreatePipe(&hStdErrReadPipe, &hStdErrWritePipe, &saAttr, 0 /*nSize*/)); RETURN_LAST_ERROR_IF(!CreatePipe(&hStdErrReadPipe, &hStdErrWritePipe, &saAttr, 0 /*nSize*/));
m_hErrReadPipe = hStdErrReadPipe; m_hErrReadPipe = hStdErrReadPipe;
@ -92,6 +108,11 @@ HRESULT PipeOutputManager::Stop()
m_disposed = true; m_disposed = true;
if (m_fCreatedConsole)
{
FreeConsole();
}
// Both pipe wrappers duplicate the pipe writer handle // Both pipe wrappers duplicate the pipe writer handle
// meaning we are fine to close the handle too. // meaning we are fine to close the handle too.
if (m_hErrWritePipe != INVALID_HANDLE_VALUE) if (m_hErrWritePipe != INVALID_HANDLE_VALUE)

View File

@ -39,6 +39,7 @@ private:
SRWLOCK m_srwLock {}; SRWLOCK m_srwLock {};
BOOL m_disposed; BOOL m_disposed;
BOOL m_fEnableNativeRedirection; BOOL m_fEnableNativeRedirection;
BOOL m_fCreatedConsole;
std::unique_ptr<StdWrapper> stdoutWrapper; std::unique_ptr<StdWrapper> stdoutWrapper;
std::unique_ptr<StdWrapper> stderrWrapper; std::unique_ptr<StdWrapper> stderrWrapper;
}; };

View File

@ -35,13 +35,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_INPROCESS_STARTUP_VALUE"] = path; deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_INPROCESS_STARTUP_VALUE"] = path;
deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_INPROCESS_RANDOM_VALUE"] = randomNumberString; deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_INPROCESS_RANDOM_VALUE"] = randomNumberString;
var deploymentResult = await DeployAsync(deploymentParameters); await AssertFailsToStart(path, deploymentParameters);
var response = await deploymentResult.HttpClient.GetAsync(path);
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
StopServer();
Assert.Contains(TestSink.Writes, context => context.Message.Contains($"Random number: {randomNumberString}")); Assert.Contains(TestSink.Writes, context => context.Message.Contains($"Random number: {randomNumberString}"));
} }
@ -56,6 +50,25 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
var deploymentParameters = _fixture.GetBaseDeploymentParameters(_fixture.StartupExceptionWebsite, publish: true); var deploymentParameters = _fixture.GetBaseDeploymentParameters(_fixture.StartupExceptionWebsite, publish: true);
deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_INPROCESS_STARTUP_VALUE"] = path; deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_INPROCESS_STARTUP_VALUE"] = path;
await AssertFailsToStart(path, deploymentParameters);
Assert.Contains(TestSink.Writes, context => context.Message.Contains(new string('a', 4096)));
}
[ConditionalFact]
public async Task CheckValidConsoleFunctions()
{
var path = "CheckConsoleFunctions";
var deploymentParameters = _fixture.GetBaseDeploymentParameters(_fixture.StartupExceptionWebsite, publish: true);
deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_INPROCESS_STARTUP_VALUE"] = path;
await AssertFailsToStart(path, deploymentParameters);
Assert.Contains(TestSink.Writes, context => context.Message.Contains("Is Console redirection: True"));
}
private async Task AssertFailsToStart(string path, IntegrationTesting.IIS.IISDeploymentParameters deploymentParameters)
{
var deploymentResult = await DeployAsync(deploymentParameters); var deploymentResult = await DeployAsync(deploymentParameters);
var response = await deploymentResult.HttpClient.GetAsync(path); var response = await deploymentResult.HttpClient.GetAsync(path);
@ -63,8 +76,6 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
StopServer(); StopServer();
Assert.Contains(TestSink.Writes, context => context.Message.Contains(new string('a', 4096)));
} }
[ConditionalFact] [ConditionalFact]

View File

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.IO;
using System.Text;
namespace IISTestSite namespace IISTestSite
{ {
@ -41,6 +43,14 @@ namespace IISTestSite
Console.Error.WriteLine(new string('a', 4096)); Console.Error.WriteLine(new string('a', 4096));
Console.Error.Flush(); Console.Error.Flush();
} }
else if (envVariable == "CheckConsoleFunctions")
{
// Call a bunch of console functions and make sure none return invalid handle.
Console.OutputEncoding = Encoding.UTF8;
Console.Title = "Test";
Console.WriteLine($"Is Console redirection: {Console.IsOutputRedirected}");
Console.BackgroundColor = ConsoleColor.Blue;
}
} }
} }
} }