diff --git a/build.psm1 b/build.psm1 index 151a33759..1a0c359e6 100644 --- a/build.psm1 +++ b/build.psm1 @@ -92,6 +92,13 @@ function Start-PSBuild { } $precheck = $precheck -and (precheck 'msbuild' 'msbuild not found. Install Visual Studio 2015.') + + #mc.exe is Message Compiler for native resources + $mcexe = Get-ChildItem "${env:ProgramFiles(x86)}\Microsoft SDKs\Windows\" -Recurse -Filter 'mc.exe' | ? {$_.FullName -match 'x64'} | select -First 1 | % {$_.FullName} + if (-not $mcexe) { + throw 'mc.exe not found. Install Microsoft Windows SDK.' + } + } elseif ($IsLinux -or $IsOSX) { foreach ($Dependency in 'cmake', 'make', 'g++') { $precheck = $precheck -and (precheck $Dependency "Build dependency '$Dependency' not found. Run Start-PSBootstrap.") @@ -175,13 +182,21 @@ function Start-PSBuild { try { Push-Location "$PSScriptRoot\src\powershell-native" + # Compile native resources + @("nativemsh/pwrshplugin") | % { + $nativeResourcesFolder = $_ + Get-ChildItem $nativeResourcesFolder -Filter "*.mc" | % { + & $mcexe -c -U $_.FullName -h $nativeResourcesFolder -r $nativeResourcesFolder + } + } + if ($cmakeGenerator) { cmake -G $cmakeGenerator . } else { cmake . } - Start-NativeExecution { msbuild powershell.vcxproj /p:Configuration=$msbuildConfiguration } + Start-NativeExecution { msbuild ALL_BUILD.vcxproj /p:Configuration=$msbuildConfiguration } } finally { Pop-Location diff --git a/src/powershell-native/.gitignore b/src/powershell-native/.gitignore index 4f159ef0a..d84d2f17a 100644 --- a/src/powershell-native/.gitignore +++ b/src/powershell-native/.gitignore @@ -8,6 +8,12 @@ x64/ ZERO_CHECK.vcxproj ZERO_CHECK.vcxproj.filters cmake_install.cmake -powershell.dir/ -powershell.vcxproj -powershell.vcxproj.filters +*.dir/ +*.vcxproj +*.vcxproj.filters +Release/ + +# Resources +MSG*.bin +pwrshpluginerrorcodes.h +pwrshpluginerrorcodes.rc \ No newline at end of file diff --git a/src/powershell-native/CMakeLists.txt b/src/powershell-native/CMakeLists.txt index 885acfcbe..a4ac5cdfa 100644 --- a/src/powershell-native/CMakeLists.txt +++ b/src/powershell-native/CMakeLists.txt @@ -16,27 +16,41 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") include_directories( - nativemsh/pwrshcommon) + nativemsh/pwrshcommon + nativemsh/pwrshplugin) link_directories( nativemsh/pwrshcommon) -add_definitions(-DUNICODE) -add_definitions(-D_UNICODE) - add_executable(powershell WIN32 nativemsh/pwrshcommon/pwrshcommon.cpp nativemsh/pwrshcommon/WinSystemCallFacade.cpp - nativemsh/pwrshexe/MainEntry.cpp) -# This subsystem definition is using old policy. TODO: figure out for release and the rest -set_target_properties(powershell PROPERTIES COMPILE_DEFINITIONS "_CONSOLE") +add_library(pwrshplugin + nativemsh/pwrshplugin/entrypoints.cpp + nativemsh/pwrshplugin/pwrshclrhost.cpp + nativemsh/pwrshplugin/pwrshpluginerrorcodes.mc + nativemsh/pwrshplugin/pwrshpluginerrorcodes.rc + nativemsh/pwrshplugin/pwrshplugin.def) + + +SET(powershell_definitions + _CONSOLE + UNICODE + _UNICODE) + +set_target_properties(powershell PROPERTIES COMPILE_DEFINITIONS "${powershell_definitions}") set_target_properties(powershell PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE") set_target_properties(powershell PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE") set_target_properties(powershell PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE") set_target_properties(powershell PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE") +SET(pwrshplugin_definitions + UNICODE + _UNICODE) + +set_target_properties(pwrshplugin PROPERTIES COMPILE_DEFINITIONS "${pwrshplugin_definitions}") target_link_libraries(powershell mscoree.lib @@ -44,3 +58,6 @@ target_link_libraries(powershell kernel32.lib msxml6.lib legacy_stdio_definitions.lib) + +target_link_libraries(pwrshplugin) + diff --git a/src/powershell-native/map.json b/src/powershell-native/map.json index cdcb1cc0e..8a19f017e 100644 --- a/src/powershell-native/map.json +++ b/src/powershell-native/map.json @@ -8,5 +8,17 @@ "monad/nttargets/assemblies/nativemsh/pwrshcommon/WinSystemCallFacade.h": "nativemsh/pwrshcommon/WinSystemCallFacade.h", "monad/nttargets/assemblies/nativemsh/pwrshcommon/IPwrshCommonOutput.h": "nativemsh/pwrshcommon/IPwrshCommonOutput.h", "monad/nttargets/assemblies/nativemsh/pwrshexe/MainEntry.cpp": "nativemsh/pwrshexe/MainEntry.cpp", - "monad/nttargets/assemblies/nativemsh/pwrshcommon/ClrHostWrapper.h": "nativemsh/pwrshcommon/ClrHostWrapper.h" + "monad/nttargets/assemblies/nativemsh/pwrshcommon/ClrHostWrapper.h": "nativemsh/pwrshcommon/ClrHostWrapper.h", + "monad/nttargets/assemblies/nativemsh/pwrshplugin/entrypoints.cpp": "nativemsh/pwrshplugin/entrypoints.cpp", + "monad/nttargets/assemblies/nativemsh/pwrshplugin/pwrshclrhost.cpp": "nativemsh/pwrshplugin/pwrshclrhost.cpp", + "monad/nttargets/assemblies/nativemsh/pwrshplugin/pwrshpluginerrorcodes.mc": "nativemsh/pwrshplugin/pwrshpluginerrorcodes.mc", + "monad/nttargets/assemblies/nativemsh/pwrshplugin/pwrshpluginResources.rc": "nativemsh/pwrshplugin/pwrshpluginResources.rc", + "monad/nttargets/assemblies/nativemsh/pwrshplugin/pwrshplugin.dll.manifest": "nativemsh/pwrshplugin/pwrshplugin.dll.manifest", + "monad/nttargets/assemblies/nativemsh/pwrshplugin/version.rc": "nativemsh/pwrshplugin/version.rc", + "monad/nttargets/assemblies/nativemsh/pwrshplugin/entrypoints.h": "nativemsh/pwrshplugin/entrypoints.h", + "monad/nttargets/assemblies/nativemsh/pwrshplugin/pwrshclrhost.h": "nativemsh/pwrshplugin/pwrshclrhost.h", + "monad/nttargets/assemblies/nativemsh/pwrshplugin/pwrshheaders.h": "nativemsh/pwrshplugin/pwrshheaders.h", + "monad/nttargets/assemblies/nativemsh/pwrshplugin/pwrshplugin.h": "nativemsh/pwrshplugin/pwrshplugin.h", + "monad/nttargets/assemblies/nativemsh/pwrshplugin/pwrshplugindefs.h": "nativemsh/pwrshplugin/pwrshplugindefs.h", + "monad/nttargets/assemblies/nativemsh/pwrshplugin/pwrshplugin.def": "nativemsh/pwrshplugin/pwrshplugin.def" } diff --git a/src/powershell-native/nativemsh/pwrshplugin/entrypoints.cpp b/src/powershell-native/nativemsh/pwrshplugin/entrypoints.cpp new file mode 100644 index 000000000..a521e0b89 --- /dev/null +++ b/src/powershell-native/nativemsh/pwrshplugin/entrypoints.cpp @@ -0,0 +1,750 @@ +// ---------------------------------------------------------------------- +// +// Microsoft Windows NT +// Copyright (C) Microsoft Corporation, 2007. +// +// Contents: Entry points for PowerShell plugin used to host powershell +// in a WSMan service. +// ---------------------------------------------------------------------- + +#include "pwrshplugin.h" +// [Porting note] SQM is for Telemetry in Windows. Temporarily disabled. +//#include +//#include "common/WindowsSqmDataID.h" +//#include "common/winrmsqm.h" + +#if !CORECLR +#include +#endif + +HINSTANCE g_hResourceInstance = 0; // TODO: Where is this freed? FreeMUILibrary for nonCoreClr and FreeLibrary for CoreCLR +LPCWSTR g_MAIN_BINARY_NAME = L"pwrshplugin.dll"; + +// [Porting note] SQM is for Telemetry in Windows. Temporarily disabled. +// typedef VOID (NTAPI *PFN_WinSqmSetDWORD)( +// __in_opt HSESSION hSession, +// __in DWORD dwDatapointId, +// __in DWORD dwDatapointValue +// ); + +// gets the error message from the resources section of the current module. +// the caller should free pwszErrorMessage using LocalFree(). +// returns: If the function succeeds the return value is the number of CHARs stored int the output +// buffer, excluding the terminating null character. If the function failes the return value is zero. +#pragma prefast(push) +#pragma prefast (disable: 28196) +DWORD GetFormattedErrorMessage(__deref_out PWSTR * pwszErrorMessage, DWORD dwMessageId, va_list* arguments) +#pragma prefast(pop) +{ + DWORD dwLength = 0; + + do + { + *pwszErrorMessage = NULL; + + if (NULL == g_hResourceInstance) + { +#ifdef CORECLR + g_hResourceInstance = LoadLibraryEx(g_MAIN_BINARY_NAME, 0, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE); +#else + g_hResourceInstance = LoadMUILibraryW(g_MAIN_BINARY_NAME, MUI_LANGUAGE_NAME, 0); +#endif + } + + LPWSTR wszSystemErrorMessage = NULL; + //string function + dwLength = FormatMessageW( + FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER, + g_hResourceInstance, + dwMessageId, + 0, + (LPWSTR)&wszSystemErrorMessage, + 0, + arguments); + + if (dwLength > 0) + { + *pwszErrorMessage = new WCHAR[dwLength + 1]; + if (NULL != *pwszErrorMessage) + { + //string function + if (FAILED(StringCchCopyW(*pwszErrorMessage, dwLength + 1, wszSystemErrorMessage))) + { + dwLength = 0; + delete [] (*pwszErrorMessage); + *pwszErrorMessage = NULL; + } + } + LocalFree(wszSystemErrorMessage); + } + + }while(false); + + return dwLength; +} + +DWORD GetFormattedErrorMessage(__deref_out PZPWSTR pwszErrorMessage, DWORD dwMessageId, ...) +{ + DWORD result = 0; + + va_list args; + va_start(args, dwMessageId); + + result = GetFormattedErrorMessage(pwszErrorMessage, dwMessageId, &args); + + va_end(args); + + return result; +} + +#pragma prefast(push) +#pragma prefast (disable: 6101) +#pragma prefast (disable: 6054) + +unsigned int ConstructPowerShellVersion(int iPSMajorVersion, + int iPSMinorVersion, + __deref_out_opt PWSTR *pwszMonadVersion) +{ + unsigned int exitCode = EXIT_CODE_SUCCESS; + wchar_t* wszMajorVersion = new wchar_t[10]; + wchar_t* wszMinorVersion = new wchar_t[10]; + + do + { + if ((NULL == pwszMonadVersion) || ( 0 > iPSMajorVersion) || (0 > iPSMinorVersion)) + { + exitCode = EXIT_CODE_BAD_INPUT; + break; + } + + if (0 != _itow_s(iPSMajorVersion, wszMajorVersion, 10, 10)) + { + exitCode = EXIT_CODE_BAD_INPUT; + break; + } + + if (0 != _itow_s(iPSMinorVersion, wszMinorVersion, 10, 10)) + { + exitCode = EXIT_CODE_BAD_INPUT; + break; + } + + size_t iMajorLength; + size_t iMinorLength; + + if (SUCCEEDED(StringCchLength(wszMajorVersion, 10, &iMajorLength)) && + SUCCEEDED(StringCchLength(wszMinorVersion, 10, &iMinorLength))) + { + size_t totalLength = iMajorLength + iMinorLength + 2; + *pwszMonadVersion = new wchar_t[totalLength]; + if (NULL == *pwszMonadVersion) + { + exitCode = ERROR_NOT_ENOUGH_MEMORY; + break; + } + + *pwszMonadVersion[0] = L'\0'; + if (SUCCEEDED(StringCchCopyW(*pwszMonadVersion, totalLength, wszMajorVersion)) && + SUCCEEDED(StringCchCatW(*pwszMonadVersion, totalLength, L".")) && + SUCCEEDED(StringCchCatW(*pwszMonadVersion, totalLength, wszMinorVersion))) + { + break; + } + else + { + exitCode = EXIT_CODE_BAD_INPUT; + break; + } + } + else + { + exitCode = EXIT_CODE_BAD_INPUT; + break; + } + }while(false); + + if (NULL != wszMajorVersion) + { + delete[] wszMajorVersion; + } + + if (NULL != wszMinorVersion) + { + delete[] wszMinorVersion; + } + + return exitCode; +} + +#pragma prefast(pop) + +static PwrshCommon sPwrshCommon; + +// Gets the CLR Version for a given PowerShell Version. PowerShell Version is +// supplied with 2 paramaters iPSMajorVersion (PowerShell major version) and +// iPSMinorVersion (PowerShell minor version). The CLR version is returned through +// pwszRuntimeVersion and pRuntimeVersionLength represents the size of pwszRuntimeVersion. +// returns: 0 on success, non-zero on failure. +_Success_(return == 0) //EXIT_CODE_SUCCESS +unsigned int GetCLRVersionForPSVersion(int iPSMajorVersion, + int iPSMinorVersion, + size_t runtimeVersionLength, + __inout_ecount_part(runtimeVersionLength, *pRuntimeVersionLength) wchar_t* pwszRuntimeVersion, + __out_ecount(1) size_t* pRuntimeVersionLength) +{ + unsigned int exitCode = EXIT_CODE_SUCCESS; + wchar_t * wszMonadVersion = NULL; + wchar_t* wszConsoleHostAssemblyName = NULL; + wchar_t* wszTempVersion = NULL; + size_t tempVersionLength = 0; + + if (NULL != pRuntimeVersionLength) + { + // Initialize the output size to zero prior to attempting the copy + *pRuntimeVersionLength = 0; + } + + do + { + int requestedMonadMajorVersion = iPSMajorVersion; + int requestedMonadMinorVersion = iPSMinorVersion; + + // For GetRegistryInfo call, monadMajorVersion is used to calculate the version key in registry. + // For PowerShell V2, version key in registry is 1. + if (2 == requestedMonadMajorVersion) + { + requestedMonadMajorVersion = 1; + } + + // For PowerShell 3, 4 and 5, the registry is 3. + if ((requestedMonadMajorVersion == 4) || (requestedMonadMajorVersion == 5)) + { + requestedMonadMajorVersion = 3; + } + + exitCode = ConstructPowerShellVersion(iPSMajorVersion, iPSMinorVersion, &wszMonadVersion); + if (EXIT_CODE_SUCCESS != exitCode) + { + break; + } + + exitCode = sPwrshCommon.GetRegistryInfo( + &wszMonadVersion, + &requestedMonadMajorVersion, + requestedMonadMinorVersion, + &wszTempVersion, + &wszConsoleHostAssemblyName); + + if (EXIT_CODE_SUCCESS != exitCode) + { + break; + } + + HRESULT hResult = StringCchLength(wszTempVersion, STRSAFE_MAX_CCH, &tempVersionLength); + + if (FAILED(hResult)) + { + exitCode = EXIT_CODE_READ_REGISTRY_FAILURE; + break; + } + + if (NULL != pwszRuntimeVersion) + { + // +1 for the '\0' + if (runtimeVersionLength < (tempVersionLength + 1)) + { + exitCode = EXIT_CODE_BAD_INPUT; + break; + } + + hResult = StringCchCopy(pwszRuntimeVersion, tempVersionLength + 1, wszTempVersion); + + if (FAILED(hResult)) + { + exitCode = EXIT_CODE_READ_REGISTRY_FAILURE; + break; + } + + if (NULL != pRuntimeVersionLength) + { + // OACR warning 26030: Postcondition violation that could result in overflow + // pRuntimeVersionLength should only be populated if the copy operation succeeded + // +1 for the '\0' + *pRuntimeVersionLength = tempVersionLength + 1; + } + } + }while(false); + + if (NULL != wszMonadVersion) + { + delete [] wszMonadVersion; + wszMonadVersion = NULL; + } + + if (NULL != wszTempVersion) + { + delete [] wszTempVersion; + wszTempVersion = NULL; + } + + if (NULL != wszConsoleHostAssemblyName) + { + delete [] wszConsoleHostAssemblyName; + wszConsoleHostAssemblyName = NULL; + } + + return exitCode; +} + +DWORD ReportOperationComplete(WSMAN_PLUGIN_REQUEST *requestDetails, DWORD errorCode) +{ + if (NULL == requestDetails) + { + // cannot report if requestDetails is NULL. + return EXIT_CODE_SUCCESS; + } + + DWORD result = EXIT_CODE_SUCCESS; + PWSTR pwszErrorMessage = NULL; + GetFormattedErrorMessage(&pwszErrorMessage, errorCode); + + result = WSManPluginOperationComplete(requestDetails, 0, errorCode, pwszErrorMessage); + + if (NULL != pwszErrorMessage) + { + delete[] pwszErrorMessage; + } + + return result; +} + +// ----------------------------------------------------------------------------- +// Each plug-in needs to support the Startup callback. A plug-in may be +// initialized more than once within the same process, but only once per +// applicationIdentification. +// ----------------------------------------------------------------------------- +DWORD WINAPI WSManPluginStartup( + __in DWORD flags, + __in PCWSTR applicationIdentification, + __in_opt PCWSTR extraInfo, + __out PVOID *pluginContext + ) +{ +// +#ifdef REMOTINGDEBUG + // This loop is added to assist debugging server. + // Attach a debugger to the server and set this variable to true + // from the debugger. + bool isDebuggerAttached = false; + do + { + Sleep(1); + }while(!isDebuggerAttached); +#endif + + PwrshPlugIn* result = NULL; + try + { + *pluginContext = NULL; + PwrshPlugInMediator* pluginMediator = PwrshPlugInMediator::GetPwrshPlugInMediator(extraInfo); + + PwrshPlugIn* result = pluginMediator->CreatePwrshPlugIn(applicationIdentification, extraInfo); + *pluginContext = (PVOID)result; + + // Using global SQM session + // WinSQMSetDWORD is not available on Vista + + // [Porting note] SQM is for Telemetry in Windows. Temporarily disabled. + // HMODULE hModule; + // PFN_WinSqmSetDWORD pfnWinSqmSetDWORD; + + // hModule = GetModuleHandleW(L"ntdll"); + // if (hModule) + // { + // pfnWinSqmSetDWORD = (PFN_WinSqmSetDWORD) GetProcAddress(hModule, "WinSqmSetDWORD"); + // if (pfnWinSqmSetDWORD) + // { + // pfnWinSqmSetDWORD( + // NULL, + // DATAID_WINRMREMOTEENABLED, + // WINRM_SQM_DATA_REMOTEENABLED + // ); + // } + // } + + return NO_ERROR; + } + catch(PlugInException* e) + { + DWORD errorId = g_CREATION_FAILED; + if (NULL != e) + { + errorId = e->dwMessageId; + delete e; + } + + if (NULL != result) + { + delete result; + } + + return errorId; + } +} + +// ------------------------------------------------------------------------------------ +//The WSManPluginShutdown method is called after all operations have been cancelled and +//right before the DLL is unloaded. The DLL entry point name must be WSManPluginShutdown. +//This method has an important purpose of making sure all plug-in threads are shut down +//before this method returns. If the plug-in only handles synchronous operations and all +//threads report a cancellation result before they return then this method does not have to +//do anything too complex other than plug-in cleanup. However for an asynchronous plug-in, +//any threads that are used to process the plug-in threads, including the ones that just reported +//the cancellation for all operations need to completely shutdown. Not doing this will cause +//potential crashes in the DLL because code may be executed after the DLL is unloaded. +// ------------------------------------------------------------------------------------ +// reason: If this is a system shutdown this will be WSMAN_PLUGIN_SHUTDOWN_SYSTEM. +// For WSMan service shutdown this will be WSMAN_PLUGIN_SHUTDOWN_SERVICE. For an IIS host +//shutdown this will be WSMAN_PLUGIN_SHUTDOWN_IISHOST. +DWORD WINAPI WSManPluginShutdown( + __in PVOID pluginContext, + __in DWORD flags, + __in DWORD reason + ) +{ + if (NULL == pluginContext) + { + return g_NULL_PLUGIN_CONTEXT; + } + + try + { + PwrshPlugInMediator* pluginMediator = PwrshPlugInMediator::GetPwrshPlugInMediator(NULL); + pluginMediator->Shutdown(flags, reason); + } + catch(PlugInException* e) + { + // ignore plugin exceptions during shutdown. + if (NULL != e) + { + delete e; + } + } + + // free resources occupied by this plugin.. + // WSMan frees shell/command resources before calling + // plugin shutdown. + PwrshPlugIn* plugIn = reinterpret_cast(pluginContext); + delete plugIn; + + return NO_ERROR; +} + +#ifndef WIN32_FROM_HRESULT +#define WIN32_FROM_HRESULT(hr) (HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : hr) +#endif + +// ----------------------------------------------------------------------------- +// A plug-in that supports the Shell operations needs to implement this callback +// to allow commands to be created and to allow data to be streamed into either +// a shell or command. The plug-in must call WSManPluginReportContext to +// report the shell context. Once the shell is completed or when it is closed +// via the operationClosed boolean value or operationClosedHandle in the +// requestDetails the plug-in needs to call WSManPluginOperationComplete. +// The shell is active until this time. +// ----------------------------------------------------------------------------- +VOID WINAPI WSManPluginShell( + __in PVOID pluginContext, + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in_opt WSMAN_SHELL_STARTUP_INFO *startupInfo, + __in_opt WSMAN_DATA *inboundShellInformation + ) +{ + PwrshPlugIn* plugIn = (NULL != pluginContext) ? (PwrshPlugIn*)pluginContext : NULL; + bool comInitialized = false; + + if (NULL == plugIn) + { + ReportOperationComplete(requestDetails, g_INVALID_PLUGIN_CONTEXT); + return; + } + + try + { + PwrshPlugInMediator* pluginMediator = PwrshPlugInMediator::GetPwrshPlugInMediator(NULL); + + HRESULT hr = CoInitializeEx(0,COINIT_MULTITHREADED); + if (hr == S_OK) + { + comInitialized = true; + } + else if (hr == S_FALSE) + { + CoUninitialize(); + comInitialized = false; + } + else if (hr == RPC_E_CHANGED_MODE) + { + comInitialized = false; //ignore + } + else + { + ReportOperationComplete(requestDetails, WIN32_FROM_HRESULT(hr)); + return; + } + + pluginMediator->CreateShell(plugIn, requestDetails, flags, startupInfo, inboundShellInformation); + + if (comInitialized) + { + CoUninitialize(); + comInitialized = false; + } + } + catch(PlugInException* e) + { + DWORD errorId = g_CREATION_FAILED; + if (NULL != e) + { + errorId = e->dwMessageId; + delete e; + } + + if (comInitialized) + { + CoUninitialize(); + comInitialized = false; + } + + ReportOperationComplete(requestDetails, errorId); + } +} + +// ----------------------------------------------------------------------------- +// WS-Man calls the WSMAN_PLUGIN_RELEASE_SHELL_CONTEXT entry point during shell +// shutdown when it is safe to delete the plug-in shell context. Any context +// reported through WSManPluginReportContext may not be deleted until the +// corresponding release function has been called. Failure to follow the contract +// will result in errors being generated. +// ----------------------------------------------------------------------------- +VOID WINAPI WSManPluginReleaseShellContext(__in PVOID shellContext) +{ + if ((NULL == shellContext)) + { + return; + } + + try + { + PwrshPlugInMediator* pluginMediator = PwrshPlugInMediator::GetPwrshPlugInMediator(NULL); + pluginMediator->ReleaseShell(shellContext); + } + catch(PlugInException* e) + { + // ignore plugin exceptions. + if (NULL != e) + { + delete e; + } + } +} + +// +// ----------------------------------------------------------------------------- +// A plug-in that supports the Shell operations and needs to create commands +// that are associated with the shell needs to implement this callback. +// The plug-in must call WSManPluginReportContext to +// report the command context. Once the command is completed or when it is closed +// via the operationClosed boolean value or operationClosedHandle in the +// requestDetails the plug-in needs to call WSManPluginOperationComplete. +// The command is active until this time. +// ----------------------------------------------------------------------------- +VOID WINAPI WSManPluginCommand( + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in PVOID shellContext, + __in PCWSTR commandLine, + __in_opt WSMAN_COMMAND_ARG_SET *arguments +) +{ + try + { + PwrshPlugInMediator* pluginMediator = PwrshPlugInMediator::GetPwrshPlugInMediator(NULL); + pluginMediator->CreateCommand(requestDetails, flags, shellContext, commandLine, arguments); + } + catch(PlugInException* e) + { + DWORD errorId = g_CREATION_FAILED; + if (NULL != e) + { + errorId = e->dwMessageId; + delete e; + } + + ReportOperationComplete(requestDetails, errorId); + } +} + +// --------------------------------------------------------------------------------- +// WS-Man calls the WSMAN_PLUGIN_RELEASE_COMMAND_CONTEXT entry point during command +// shutdown when it is safe to delete the plug-in shell context. Any context +// reported through WSManPluginReportContext may not be deleted until the +// corresponding release function has been called. Failure to follow the contract +// will result in errors being generated. +// --------------------------------------------------------------------------------- +VOID WINAPI WSManPluginReleaseCommandContext( + __in PVOID shellContext, + __in PVOID commandContext + ) +{ + if ((NULL == shellContext) || (NULL == commandContext)) + { + return; + } + + try + { + PwrshPlugInMediator* pluginMediator = PwrshPlugInMediator::GetPwrshPlugInMediator(NULL); + pluginMediator->ReleaseCommand(shellContext, commandContext); + } + catch(PlugInException* e) + { + // ignore plugin exceptions. + if (NULL != e) + { + delete e; + } + } +} + +// ----------------------------------------------------------------------------- +// A plug-in receives an inbound data stream to either the shell or command +// via this callback. Each piece of data causes the callback to be called once. +// For each piece of data the plug-in calls WSManPluginResultComplete to +// acknowledge receipt and to allow the next piece of data to be delivered. +// ----------------------------------------------------------------------------- +VOID WINAPI WSManPluginSend( + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in PVOID shellContext, + __in_opt PVOID commandContext, + __in PCWSTR stream, + __in WSMAN_DATA *inboundData + ) +{ + try + { + PwrshPlugInMediator* pluginMediator = PwrshPlugInMediator::GetPwrshPlugInMediator(NULL); + pluginMediator->SendOneItemToShellOrCommand(requestDetails, flags, shellContext, commandContext, stream, inboundData); + } + catch(PlugInException* e) + { + DWORD errorId = g_INVALID_PLUGIN_CONTEXT; + if (NULL != e) + { + errorId = e->dwMessageId; + delete e; + } + + ReportOperationComplete(requestDetails, errorId); + } +} + +// ----------------------------------------------------------------------------- +// A plug-in sends an outbound data stream from either the shell or command +// via this callback. This API is called when an inbound request from a client +// is received. This callback may be called against the shell and/or command +// based on the client request. Each piece of data that needs to be sent back +// to the client is done so through the WSManPluginReceiveResult API. Once +// all data has been send, when the stream is terminated via some internal means, +// or if the receive call is cancelled through the operationClosed boolean +// value or operationClosedHandle, the plug-in needs to call +// WSManPluginResultComplete. The operation is marked as active until this +// time. +// ----------------------------------------------------------------------------- +VOID WINAPI WSManPluginReceive( + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in PVOID shellContext, + __in_opt PVOID commandContext, + __in_opt WSMAN_STREAM_ID_SET *streamSet + ) +{ + try + { + PwrshPlugInMediator* pluginMediator = PwrshPlugInMediator::GetPwrshPlugInMediator(NULL); + pluginMediator->EnableShellOrCommandToSendDataToClient(requestDetails, flags, shellContext, commandContext, streamSet); + } + catch(PlugInException* e) + { + DWORD errorId = g_INVALID_PLUGIN_CONTEXT; + if (NULL != e) + { + errorId = e->dwMessageId; + delete e; + } + + ReportOperationComplete(requestDetails, errorId); + } +} + +// ----------------------------------------------------------------------------- +// A plug-in receives an inbound signal to either the shell or command +// via this callback. Each signal causes the callback to be called once. +// For each callthe plug-in calls WSManPluginResultComplete to +// acknowledge receipt and to allow the next signal to be received. +// A signal can cause the shell or command to be terminated, so the result +// of this callback may be many completion calls for the Signal, Receive, Command +// and Shell operations. +// ----------------------------------------------------------------------------- +VOID WINAPI WSManPluginSignal( + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in PVOID shellContext, + __in_opt PVOID commandContext, + __in PCWSTR code) +{ + try + { + PwrshPlugInMediator* pluginMediator = PwrshPlugInMediator::GetPwrshPlugInMediator(NULL); + pluginMediator->SignalShellOrCmd(requestDetails, flags, shellContext, commandContext, code); + } + catch(PlugInException* e) + { + DWORD errorId = g_INVALID_PLUGIN_CONTEXT; + if (NULL != e) + { + errorId = e->dwMessageId; + delete e; + } + + ReportOperationComplete(requestDetails, errorId); + } +} + + +VOID WINAPI WSManPluginConnect( + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in PVOID shellContext, + __in_opt PVOID commandContext, + __in_opt WSMAN_DATA *inboundConnectInformation) +{ + try + { + PwrshPlugInMediator* pluginMediator = PwrshPlugInMediator::GetPwrshPlugInMediator(NULL); + pluginMediator->ExecuteConnectToShellOrCommand(requestDetails, flags, shellContext, commandContext, inboundConnectInformation); + } + catch(PlugInException* e) + { + DWORD errorId = g_INVALID_PLUGIN_CONTEXT; + if (NULL != e) + { + errorId = e->dwMessageId; + delete e; + } + + ReportOperationComplete(requestDetails, errorId); + } +} diff --git a/src/powershell-native/nativemsh/pwrshplugin/entrypoints.h b/src/powershell-native/nativemsh/pwrshplugin/entrypoints.h new file mode 100644 index 000000000..bd2e92d7a --- /dev/null +++ b/src/powershell-native/nativemsh/pwrshplugin/entrypoints.h @@ -0,0 +1,18 @@ +// Microsoft Windows NT +// Copyright (C) Microsoft Corporation, 2007. +// +// Contents: Headers used by pwrshplugin. +// pwrshplugin is totally unmanaged. +// ---------------------------------------------------------------------- + +#pragma once + +#include +#include +#include "pwrshpluginerrorcodes.h" + +const int EXIT_CODE_BAD_INPUT = 100; + +extern DWORD GetFormattedErrorMessage(__deref_out PWSTR * pwszErrorMessage, DWORD dwMessageId, ...); +extern unsigned int ConstructPowerShellVersion(int iPSMajorVersion, int iPSMinorVersion, __deref_out_opt PWSTR *pwszMonadVersion); + diff --git a/src/powershell-native/nativemsh/pwrshplugin/pwrshclrhost.cpp b/src/powershell-native/nativemsh/pwrshplugin/pwrshclrhost.cpp new file mode 100644 index 000000000..db5a72c3d --- /dev/null +++ b/src/powershell-native/nativemsh/pwrshplugin/pwrshclrhost.cpp @@ -0,0 +1,472 @@ +// ---------------------------------------------------------------------- +// +// Microsoft Windows NT +// Copyright (C) Microsoft Corporation, 2014. +// +// Contents: Source code for abstraction of CLR and worker differences between PowerShell versions. +// pwrshplugin is totally unmanaged. +// ---------------------------------------------------------------------- + +#pragma once + +#include "pwrshclrhost.h" +#include "NativeMsh.h" +#include "entrypoints.h" +#include "WinSystemCallFacade.h" + +#if !CORECLR + +// include the tlb for mscorlib for access to the default AppDomain through COM Interop +#import raw_interfaces_only high_property_prefixes("_get","_put","_putref")\ + rename("ReportEvent", "CLRReportEvent") + +using namespace mscorlib; + +#endif + +using namespace NativeMsh; + +// Init function calls are handled internally here within the context of the mediator singleton +typedef void (WINAPI *InitPluginFuncPtr)(); // Original PS init +typedef DWORD (WINAPI *InitPluginWkrPtrsFuncPtr)(__out PwrshPluginWkr_Ptrs* wkrPtrs); // Updated PS init + +#ifdef CORECLR + +// Define the function pointer for the powershell entry point. +typedef int (STDMETHODCALLTYPE *MonadRunHelperFp)(int length, LPCWSTR* commands); + +// Define the function pointer for AssemblyLoadContext initializer. +typedef void (STDMETHODCALLTYPE *LoaderRunHelperFp)(LPCWSTR appPath); + +unsigned int PowerShellCoreClrWorker::LaunchClr( + _In_ LPCWSTR wszMonadVersion, + _In_ LPCWSTR wszRuntimeVersion) +{ + return commonLib->LaunchCoreCLR(hostWrapper, hostEnvironment); +} + +unsigned int PowerShellCoreClrWorker::LoadWorkerCallbackPtrs( + _In_ PwrshPluginWkr_Ptrs* workerCallbackPtrs, + _In_z_ wchar_t* wszMgdPlugInFileName, + _Outptr_result_maybenull_ PlugInException** pPluginException) +{ + unsigned int exitCode = EXIT_CODE_SUCCESS; + + *pPluginException = NULL; + + exitCode = commonLib->CreateAppDomain(hostWrapper, L"PwrshPlugin", hostEnvironment); + if (EXIT_CODE_SUCCESS != exitCode) + { + return exitCode; + } + + // Set the powershell custom assembly loader to be the default + LoaderRunHelperFp initDelegate = NULL; + HRESULT hr = hostWrapper->CreateDelegate( + hostWrapper->GetAppDomainId(), + L"Microsoft.PowerShell.CoreCLR.AssemblyLoadContext, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + L"System.Management.Automation.PowerShellAssemblyLoadContextInitializer", + L"SetPowerShellAssemblyLoadContext", + (INT_PTR*)&initDelegate); + + if (FAILED(hr)) + { + output->DisplayMessage(false, g_CREATING_MSH_ENTRANCE_FAILED, hr); + } + else + { + initDelegate(hostEnvironment.GetHostDirectoryPath()); + } + + // Call into powershell entry point + InitPluginWkrPtrsFuncPtr entryPointDelegate = NULL; + + // Create the function pointer for the managed entry point + // It must be targeted at a static method in the managed code. + hr = hostWrapper->CreateDelegate( + hostWrapper->GetAppDomainId(), + L"System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", + L"System.Management.Automation.Remoting.WSManPluginManagedEntryWrapper", + L"InitPlugin", + (INT_PTR*)&entryPointDelegate); + if (FAILED(hr)) + { + //LONG systemErrorCode = GetLastError(); + output->DisplayMessage(false, g_CREATING_MSH_ENTRANCE_FAILED, hr); + + return EXIT_CODE_INIT_FAILURE; + } + + // Passes empty pointer structure to the function, expects a filled struct if successful + return entryPointDelegate(workerCallbackPtrs); +} + +PowerShellCoreClrWorker::PowerShellCoreClrWorker() + : systemCalls(new WinSystemCallFacade()), + hostWrapper(new ICLRRuntimeHost2Wrapper()), + output(new PwrshPluginOutputDefault()), + commonLib(new PwrshCommon()) +{ +} + +// +// sysCalls is expected to be new'd by the caller. +// It will be freed in PowerShellCoreClrWorker's destructor. +// +PowerShellCoreClrWorker::PowerShellCoreClrWorker( + SystemCallFacade* sysCalls, + ClrHostWrapper* hstWrp, + PwrshCommon* cmnLib) + : systemCalls(sysCalls), + hostWrapper(hstWrp), + output(new PwrshPluginOutputDefault()), + commonLib(cmnLib) +{ + if (NULL == systemCalls) + { + // Instantiate it even if one is not provided to guarantee that it will + // always be non-NULL during execution. + systemCalls = new WinSystemCallFacade(); + } + + if (NULL == hostWrapper) + { + // Instantiate it even if one is not provided to guarantee that it will + // always be non-NULL during execution. + hostWrapper = new ICLRRuntimeHost2Wrapper(); + } + + if (NULL == commonLib) + { + commonLib = new PwrshCommon(new PwrshPluginOutputDefault(), new WinSystemCallFacade()); + } +} + +PowerShellCoreClrWorker::~PowerShellCoreClrWorker() +{ + unsigned int exitCode = hostWrapper->CleanUpHostWrapper(); + if (EXIT_CODE_SUCCESS != exitCode) + { + output->DisplayMessage(false, exitCode); + } + + if (systemCalls) + { + delete systemCalls; + systemCalls = NULL; + } + + if (hostWrapper) + { + delete hostWrapper; + hostWrapper = NULL; + } + + if (output) + { + delete output; + output = NULL; + } + + if (commonLib) + { + delete commonLib; + commonLib = NULL; + } +} + +#else // !CORECLR + +PowerShellClrWorker::PowerShellClrWorker() : + pHost(NULL), + hManagedPluginModule(NULL), + systemCalls(new WinSystemCallFacade()), + g_INIT_PLUGIN("InitPlugin"), + g_SHUTDOWN_PLUGIN("ShutdownPlugin"), + g_MANAGED_PLUGIN_CREATE_SHELL("WSManPluginShell"), + g_MANAGED_PLUGIN_RELEASE_SHELL("WSManPluginReleaseShellContext"), + g_MANAGED_PLUGIN_CREATE_COMMAND("WSManPluginCommand"), + g_MANAGED_PLUGIN_RELEASE_COMMAND("WSManPluginReleaseCommandContext"), + g_MANAGED_PLUGIN_SEND("WSManPluginSend"), + g_MANAGED_PLUGIN_RECEIVE("WSManPluginReceive"), + g_MANAGED_PLUGIN_SIGNAL("WSManPluginSignal"), + g_MANAGED_PLUGIN_CONNECT("WSManPluginConnect"), + output(new PwrshPluginOutputDefault()) +{} + +PowerShellClrWorker::PowerShellClrWorker( + SystemCallFacade* sysCalls) + : pHost(NULL), + hManagedPluginModule(NULL), + systemCalls(sysCalls), + g_INIT_PLUGIN("InitPlugin"), + g_SHUTDOWN_PLUGIN("ShutdownPlugin"), + g_MANAGED_PLUGIN_CREATE_SHELL("WSManPluginShell"), + g_MANAGED_PLUGIN_RELEASE_SHELL("WSManPluginReleaseShellContext"), + g_MANAGED_PLUGIN_CREATE_COMMAND("WSManPluginCommand"), + g_MANAGED_PLUGIN_RELEASE_COMMAND("WSManPluginReleaseCommandContext"), + g_MANAGED_PLUGIN_SEND("WSManPluginSend"), + g_MANAGED_PLUGIN_RECEIVE("WSManPluginReceive"), + g_MANAGED_PLUGIN_SIGNAL("WSManPluginSignal"), + g_MANAGED_PLUGIN_CONNECT("WSManPluginConnect"), + output(new PwrshPluginOutputDefault()) +{ + if (NULL == systemCalls) + { + systemCalls = new WinSystemCallFacade(); + } +} + +PowerShellClrWorker::~PowerShellClrWorker() +{ + if (NULL != hManagedPluginModule) + { + FreeLibrary(hManagedPluginModule); + hManagedPluginModule = NULL; + } + + if (output) + { + delete output; + output = NULL; + } + + if (systemCalls) + { + delete systemCalls; + systemCalls = NULL; + } +} + +unsigned int PowerShellClrWorker::LaunchClr( + _In_ LPCWSTR wszMonadVersion, + _In_ LPCWSTR wszRuntimeVersion) +{ + return commonLib.LaunchCLR(wszMonadVersion, wszRuntimeVersion, &pHost); +} + +unsigned int PowerShellClrWorker::LoadWorkerCallbackPtrs( + _In_ PwrshPluginWkr_Ptrs* workerCallbackPtrs, + _In_z_ wchar_t* wszMgdPlugInFileName, + _Outptr_result_maybenull_ PlugInException** pPluginException) +{ + *pPluginException = NULL; +#pragma prefast(push) +#pragma prefast (disable: 28752) + if (wszMgdPlugInFileName) + { + hManagedPluginModule = systemCalls->LoadLibraryExW(wszMgdPlugInFileName, NULL, 0); + } +#pragma prefast(pop) + + unsigned int exitCode = EXIT_CODE_SUCCESS; + + if (NULL == hManagedPluginModule) + { + // Get Extended error information.. to know why load failed. + PWSTR msg = NULL; + exitCode = g_MANAGED_PLUGIN_LOAD_FAILED; + commonLib.GetSystemErrorMessage(GetLastError(), &msg); + *pPluginException = new PlugInException(exitCode, msg); + return exitCode; + } + + // Now get the proc address for all the plugin API. + // Init is handled differently because it is not used in the updated + // worker module. + InitPluginFuncPtr hInitPluginMethodAddress = (InitPluginFuncPtr)systemCalls->GetProcAddress(hManagedPluginModule, g_INIT_PLUGIN); + + // This is not strictly necessary (they can be directly assigned), but I am doing this + // to keep all the code paths common + workerCallbackPtrs->shutdownPluginFuncPtr = (ShutdownPluginFuncPtr)systemCalls->GetProcAddress(hManagedPluginModule, g_SHUTDOWN_PLUGIN); + workerCallbackPtrs->wsManPluginShellFuncPtr = (WSManPluginShellFuncPtr)systemCalls->GetProcAddress(hManagedPluginModule, g_MANAGED_PLUGIN_CREATE_SHELL); + workerCallbackPtrs->wsManPluginReleaseShellContextFuncPtr = (WSManPluginReleaseShellContextFuncPtr)systemCalls->GetProcAddress(hManagedPluginModule, g_MANAGED_PLUGIN_RELEASE_SHELL); + workerCallbackPtrs->wsManPluginCommandFuncPtr = (WSManPluginCommandFuncPtr)systemCalls->GetProcAddress(hManagedPluginModule, g_MANAGED_PLUGIN_CREATE_COMMAND); + workerCallbackPtrs->wsManPluginReleaseCommandContextFuncPtr = (WSManPluginReleaseCommandContextFuncPtr)systemCalls->GetProcAddress(hManagedPluginModule, g_MANAGED_PLUGIN_RELEASE_COMMAND); + workerCallbackPtrs->wsManPluginSendFuncPtr = (WSManPluginSendFuncPtr)systemCalls->GetProcAddress(hManagedPluginModule, g_MANAGED_PLUGIN_SEND); + workerCallbackPtrs->wsManPluginReceiveFuncPtr = (WSManPluginReceiveFuncPtr)systemCalls->GetProcAddress(hManagedPluginModule, g_MANAGED_PLUGIN_RECEIVE); + workerCallbackPtrs->wsManPluginSignalFuncPtr = (WSManPluginSignalFuncPtr)systemCalls->GetProcAddress(hManagedPluginModule, g_MANAGED_PLUGIN_SIGNAL); + workerCallbackPtrs->wsManPluginConnectFuncPtr = (WSManPluginConnectFuncPtr)systemCalls->GetProcAddress(hManagedPluginModule, g_MANAGED_PLUGIN_CONNECT); + + // Initialize the loaded module (pspluginwkr.dll) + if (hInitPluginMethodAddress) + { + // Only the "old" version of the worker requires a stand-alone init call. + // This object is a member of the PwrshPlugInMediator singleton, so this + // call is made from that context. + (hInitPluginMethodAddress)(); + } + else + { + PWSTR msg = NULL; + exitCode = g_MANAGED_METHOD_RESOLUTION_FAILED; + GetFormattedErrorMessage(&msg, exitCode); + *pPluginException = new PlugInException(exitCode, msg); + return exitCode; + } + + return exitCode; +} + +unsigned int PowerShellClrManagedWorker::LaunchClr( + _In_ LPCWSTR wszMonadVersion, + _In_ LPCWSTR wszRuntimeVersion) +{ + return commonLib.LaunchCLR(wszMonadVersion, wszRuntimeVersion, &pHost); +} + +unsigned int PowerShellClrManagedWorker::LoadWorkerCallbackPtrs( + _In_ PwrshPluginWkr_Ptrs* workerCallbackPtrs, + _In_z_ wchar_t* wszMgdPlugInFileName, + _Outptr_result_maybenull_ PlugInException** pPluginException) +{ + unsigned int exitCode = EXIT_CODE_SUCCESS; + HRESULT hr = S_OK; + *pPluginException = NULL; + + do + { + // Get a pointer to the default AppDomain + CComPtr<_AppDomain> spDefaultDomain = NULL; + CComPtr spAppDomainPunk = NULL; + + hr = pHost->GetDefaultDomain(&spAppDomainPunk); + if (FAILED(hr) || spAppDomainPunk == NULL) + { + output->DisplayMessage(false, g_GETTING_DEFAULT_DOMAIN_FAILED, hr); + exitCode = EXIT_CODE_INIT_FAILURE; + break; + } + + hr = spAppDomainPunk->QueryInterface(__uuidof(_AppDomain), (PVOID*)&spDefaultDomain); + if (FAILED(hr) || spDefaultDomain == NULL) + { + output->DisplayMessage(false, g_GETTING_DEFAULT_DOMAIN_FAILED, hr); + exitCode = EXIT_CODE_INIT_FAILURE; + break; + } + + CComPtr<_ObjectHandle> spObjectHandle; + + // use CreateInstance because we use the assembly strong name (as opposed to CreateInstanceFrom) + // + // wszMgdPlugInFileName is %systemdir%\Windows\System32\WindowsPowerShell\v1.0\system.management.automation.dll. + _bstr_t bstrConsoleHostAssemblyName = _bstr_t(L"System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); + _bstr_t bstrUnmanagedMshEntryClass = _bstr_t(L"System.Management.Automation.Remoting.WSManPluginManagedEntryInstanceWrapper"); + + hr = spDefaultDomain->CreateInstance( + bstrConsoleHostAssemblyName, + bstrUnmanagedMshEntryClass, + &spObjectHandle); + if (FAILED(hr) || spObjectHandle == NULL) + { + output->DisplayMessage(false, g_CREATING_MSH_ENTRANCE_FAILED, hr); + exitCode = EXIT_CODE_INIT_FAILURE; + break; + } + + CComVariant VntUnwrapped; + hr = spObjectHandle->Unwrap(&VntUnwrapped); + if (FAILED(hr)) + { + output->DisplayMessage(false, g_CREATING_MSH_ENTRANCE_FAILED, hr); + exitCode = EXIT_CODE_INIT_FAILURE; + break; + } + + CComPtr pDisp; + pDisp = VntUnwrapped.pdispVal; + + OLECHAR FAR * wszMember = L"GetEntryDelegate"; + + DISPID dispid; + //Retrieve the DISPID + hr = pDisp->GetIDsOfNames( + IID_NULL, + &wszMember, + 1, + LOCALE_SYSTEM_DEFAULT, + &dispid); + + if (FAILED(hr)) + { + output->DisplayMessage(false, g_GETTING_DISPATCH_ID_FAILED, hr); + exitCode = EXIT_CODE_INIT_FAILURE; + break; + } + + DISPPARAMS dispparamsOneArg = { NULL, NULL, 0 }; + VARIANT varResult; + VariantInit(&varResult); + EXCEPINFO exception; + unsigned int uArgErr = 0; + + //Invoke the method on the Dispatch Interface + hr = pDisp->Invoke( + dispid, + IID_NULL, + LOCALE_SYSTEM_DEFAULT, + DISPATCH_METHOD, + &dispparamsOneArg, + &varResult, + &exception, + &uArgErr); + + InitPluginWkrPtrsFuncPtr entryPointDelegate = (InitPluginWkrPtrsFuncPtr)varResult.byref; + + if (FAILED(hr) || + NULL == entryPointDelegate) + { + if (DISP_E_EXCEPTION == hr) + { + output->DisplayMessage(false, g_MANAGED_MSH_EXCEPTION, exception.bstrDescription); + } + else + { + output->DisplayMessage(false, g_INOVKING_MSH_ENTRANCE_FAILED, hr); + } + exitCode = EXIT_CODE_INIT_FAILURE; + break; + } + + // Passes empty pointer structure to the function, expects a filled struct if successful + exitCode = entryPointDelegate(workerCallbackPtrs); + } + while (false); + + return exitCode; +} + +PowerShellClrManagedWorker::~PowerShellClrManagedWorker() +{ + // No need to call pHost->Stop() because, + // as the common language runtime is automatically unloaded when the process exits. + if (output) + { + delete output; + output = NULL; + } +} + +#endif + +// +// This factory method analyzes the wszMgdPlugInFileName and determines the +// appropriate IPowerShellClrHost to instantiate. It is the caller's responsibility +// to free the IPowerShellClrHost once finished using it. +// +IPowerShellClrHost* PowerShellClrWorkerFactory( + _In_ PWSTR wszMgdPlugInFileName) +{ +#ifdef CORECLR + return new PowerShellCoreClrWorker(); // Calls into System.Management.Automation.dll +#else // !CORECLR + std::wstring pluginFileName(wszMgdPlugInFileName); + // This is a case-sensitive search. It checks to see if the file name portion of the path matches the v3 module name. + if (std::wstring::npos != pluginFileName.rfind(g_MANAGED_PLUGIN_FILENAME_V3_STRING)) + { + return new PowerShellClrManagedWorker(); // Calls into System.Management.Automation.dll + } + return new PowerShellClrWorker(); // Calls into pspluginwkr.dll (previous generation of plugin worker) +#endif // !CORECLR +} diff --git a/src/powershell-native/nativemsh/pwrshplugin/pwrshclrhost.h b/src/powershell-native/nativemsh/pwrshplugin/pwrshclrhost.h new file mode 100644 index 000000000..50ea8258e --- /dev/null +++ b/src/powershell-native/nativemsh/pwrshplugin/pwrshclrhost.h @@ -0,0 +1,165 @@ +// ---------------------------------------------------------------------- +// +// Microsoft Windows NT +// Copyright (C) Microsoft Corporation, 2007. +// +// Contents: Headers used by pwrshplugin. +// pwrshplugin is totally unmanaged. +// ---------------------------------------------------------------------- + +#pragma once + +#include "pwrshplugindefs.h" // PwrshPluginWkr_Ptrs and PlugInException +#include +#include +#include +#include "SystemCallFacade.h" +#include "ClrHostWrapper.h" +#include "NativeMsh.h" + +class PwrshPluginOutputDefault : public NativeMsh::IPwrshCommonOutput +{ +public: + virtual VOID DisplayMessage( + bool bUseStdOut, + DWORD dwMessageId, + ...) + { + return; + } + + virtual void DisplayErrorWithSystemError( + LONG lSystemErrorCode, + int messageId, + LPCWSTR insertionParam) + { + return; + } +}; + +class IPowerShellClrHost +{ +public: + // Virtual destructor to ensure that derived destructors are called + // during base class destruction. + virtual ~IPowerShellClrHost() {} + + virtual unsigned int LaunchClr( + _In_ LPCWSTR wszMonadVersion, + _In_ LPCWSTR wszRuntimeVersion) = 0; + + virtual unsigned int LoadWorkerCallbackPtrs( + _In_ PwrshPluginWkr_Ptrs* workerCallbackPtrs, + _In_z_ PWSTR wszMgdPlugInFileName, + _Outptr_result_maybenull_ PlugInException** pPluginException) = 0; +}; + +#if !CORECLR // See note for LaunchCLR for an explanation. + +class PowerShellClrWorker : public IPowerShellClrHost +{ +private: + CComPtr pHost; + HMODULE hManagedPluginModule; + NativeMsh::IPwrshCommonOutput* output; + NativeMsh::PwrshCommon commonLib; + + const LPCSTR g_INIT_PLUGIN; + const LPCSTR g_SHUTDOWN_PLUGIN; + const LPCSTR g_MANAGED_PLUGIN_CREATE_SHELL; + const LPCSTR g_MANAGED_PLUGIN_RELEASE_SHELL; + const LPCSTR g_MANAGED_PLUGIN_CREATE_COMMAND; + const LPCSTR g_MANAGED_PLUGIN_RELEASE_COMMAND; + const LPCSTR g_MANAGED_PLUGIN_SEND; + const LPCSTR g_MANAGED_PLUGIN_RECEIVE; + const LPCSTR g_MANAGED_PLUGIN_SIGNAL; + const LPCSTR g_MANAGED_PLUGIN_CONNECT; + + NativeMsh::SystemCallFacade* systemCalls; + +public: + PowerShellClrWorker(); + PowerShellClrWorker(NativeMsh::SystemCallFacade* sysCalls); + + virtual ~PowerShellClrWorker(); + + // + // IPowerShellClrHost Methods + // + virtual unsigned int LaunchClr( + _In_ LPCWSTR wszMonadVersion, + _In_ LPCWSTR wszRuntimeVersion); + + virtual unsigned int LoadWorkerCallbackPtrs( + _In_ PwrshPluginWkr_Ptrs* workerCallbackPtrs, + _In_z_ wchar_t* wszMgdPlugInFileName, + _Outptr_result_maybenull_ PlugInException** pPluginException); +}; + +class PowerShellClrManagedWorker : public IPowerShellClrHost +{ +private: + CComPtr pHost; + NativeMsh::IPwrshCommonOutput* output; + NativeMsh::PwrshCommon commonLib; + +public: + PowerShellClrManagedWorker() : pHost(NULL), output(new PwrshPluginOutputDefault()) + {} + + virtual ~PowerShellClrManagedWorker(); + + // + // IPowerShellClrHost Methods + // + virtual unsigned int LaunchClr( + _In_ LPCWSTR wszMonadVersion, + _In_ LPCWSTR wszRuntimeVersion); + + virtual unsigned int LoadWorkerCallbackPtrs( + _In_ PwrshPluginWkr_Ptrs* workerCallbackPtrs, + _In_z_ wchar_t* wszMgdPlugInFileName, + _Outptr_result_maybenull_ PlugInException** pPluginException); +}; + +#else // CORECLR + +class PowerShellCoreClrWorker : public IPowerShellClrHost +{ +private: + NativeMsh::ClrHostWrapper* hostWrapper; + NativeMsh::SystemCallFacade* systemCalls; + NativeMsh::PwrshCommon* commonLib; + NativeMsh::IPwrshCommonOutput* output; + NativeMsh::HostEnvironment hostEnvironment; + +public: + PowerShellCoreClrWorker(); + + PowerShellCoreClrWorker( + NativeMsh::SystemCallFacade* sysCalls, + NativeMsh::ClrHostWrapper* hstWrp, + NativeMsh::PwrshCommon* cmnLib); + + virtual ~PowerShellCoreClrWorker(); + + std::wstring GetHostDirectory() { return std::wstring(hostEnvironment.GetHostDirectoryPath()); } + std::wstring GetClrDirectory() { return std::wstring(hostEnvironment.GetCoreCLRDirectoryPath()); } + + // + // IPowerShellClrHost Methods + // + virtual unsigned int LaunchClr( + _In_ LPCWSTR wszMonadVersion, + _In_ LPCWSTR wszRuntimeVersion); + + virtual unsigned int LoadWorkerCallbackPtrs( + _In_ PwrshPluginWkr_Ptrs* workerCallbackPtrs, + _In_z_ PWSTR wszMgdPlugInFileName, + _Outptr_result_maybenull_ PlugInException** pPluginException); +}; + +#endif // CORECLR + +IPowerShellClrHost* PowerShellClrWorkerFactory( + _In_ PWSTR wszMgdPlugInFileName); diff --git a/src/powershell-native/nativemsh/pwrshplugin/pwrshheaders.h b/src/powershell-native/nativemsh/pwrshplugin/pwrshheaders.h new file mode 100644 index 000000000..d63dc56cb --- /dev/null +++ b/src/powershell-native/nativemsh/pwrshplugin/pwrshheaders.h @@ -0,0 +1,22 @@ +// ---------------------------------------------------------------------- +// +// Microsoft Windows NT +// Copyright (C) Microsoft Corporation, 2008. +// +// Contents: Headers used by internal windows teams to access certain +// Powershell functionality +// ---------------------------------------------------------------------- + +#pragma once + +// Gets the CLR Version for a given PowerShell Version. PowerShell Version is +// supplied with 2 paramaters iPSMajorVersion (PowerShell major version) and +// iPSMinorVersion (PowerShell minor version). The CLR version is returned through +// pwszRuntimeVersion and pRuntimeVersionLength represents the size of pwszRuntimeVersion. +// returns: 0 on success, non-zero on failure. +_Success_(return == 0) // EXIT_CODE_SUCCESS +extern unsigned int GetCLRVersionForPSVersion(int iPSMajorVersion, + int iPSMinorVersion, + size_t runtimeVersionLength, + __inout_ecount_part(runtimeVersionLength , *pRuntimeVersionLength) wchar_t* pwszRuntimeVersion, + __out_ecount(1) size_t* pRuntimeVersionLength); \ No newline at end of file diff --git a/src/powershell-native/nativemsh/pwrshplugin/pwrshplugin.def b/src/powershell-native/nativemsh/pwrshplugin/pwrshplugin.def new file mode 100644 index 000000000..f80ff8ac2 --- /dev/null +++ b/src/powershell-native/nativemsh/pwrshplugin/pwrshplugin.def @@ -0,0 +1,18 @@ +LIBRARY pwrshplugin + +EXPORTS + +GetCLRVersionForPSVersion + +WSManPluginStartup +WSManPluginShutdown + +WSManPluginShell +WSManPluginReleaseShellContext +WSManPluginCommand +WSManPluginReleaseCommandContext +WSManPluginSend +WSManPluginSignal +WSManPluginReceive +WSManPluginConnect +PerformWSManPluginReportCompletion \ No newline at end of file diff --git a/src/powershell-native/nativemsh/pwrshplugin/pwrshplugin.dll.manifest b/src/powershell-native/nativemsh/pwrshplugin/pwrshplugin.dll.manifest new file mode 100644 index 000000000..ef7b2351c --- /dev/null +++ b/src/powershell-native/nativemsh/pwrshplugin/pwrshplugin.dll.manifest @@ -0,0 +1,17 @@ + + + PowerShell Plugin + + + + + + + + diff --git a/src/powershell-native/nativemsh/pwrshplugin/pwrshplugin.h b/src/powershell-native/nativemsh/pwrshplugin/pwrshplugin.h new file mode 100644 index 000000000..ab7c6d267 --- /dev/null +++ b/src/powershell-native/nativemsh/pwrshplugin/pwrshplugin.h @@ -0,0 +1,1101 @@ +// ---------------------------------------------------------------------- +// +// Microsoft Windows NT +// Copyright (C) Microsoft Corporation, 2007. +// +// Contents: Headers used by pwrshplugin. +// pwrshplugin is totally unmanaged. +// ---------------------------------------------------------------------- + +#pragma once + +#include "NativeMsh.h" +#include "NativeMshConstants.h" +#include "entrypoints.h" +#include "pwrshheaders.h" +#include "pwrshplugindefs.h" +#include "pwrshclrhost.h" + +// include wsman.h header file..it is required to declare the version +// number of the wsman API to use +#define WSMAN_API_VERSION_1_0 +#include + +using namespace NativeMsh; + +// Forward declaration of class PwrshPlugIn +class PwrshPlugIn; + +// To report the plugin completion using WSManPluginReportCompletion API +// g_pPluginContext MUST be the same context that plugin provided to the WSManPluginStartup method +PwrshPlugIn* g_pPluginContext; + +class PwrshPlugIn +{ +private: + PCWSTR pAppIdentifier; + PCWSTR initParameters; + +public: + // applicationIdentification: + // This relates to the application HTTP suffix that is hosting the plug-in. + // For the main WSMan service by default this would be "wsman", whereas for + // an IIS host it would relate to the application endpoint for that host and + // would be something like "MyCompany/MyApplication". + PwrshPlugIn(PCWSTR applicationIdentification, PCWSTR pInitParams) + { + pAppIdentifier = applicationIdentification; + initParameters = pInitParams; + } + + ~PwrshPlugIn() + { + if (NULL != initParameters) + { + delete[] initParameters; + } + } + + PCWSTR GetApplicationIdentifier() + { + return pAppIdentifier; + } + + PCWSTR GetInitParameters() + { + return initParameters; + } +}; + +class PwrshPlugInMediator +{ +private: + // handle to managed powershell plugin + ShutdownPluginFuncPtr hShutdownPluginMethodAddress; + WSManPluginShellFuncPtr hCreateShellMethodAddress; + WSManPluginReleaseShellContextFuncPtr hReleaseShellMethodAddress; + WSManPluginCommandFuncPtr hCreateCommandMethodAddress; + WSManPluginReleaseCommandContextFuncPtr hReleaseCommandMethodAddress; + WSManPluginSendFuncPtr hSendMethodAddress; + WSManPluginReceiveFuncPtr hReceiveMethodAddress; + WSManPluginSignalFuncPtr hSignalMethodAddress; + WSManPluginConnectFuncPtr hConnectMethodAddress; + + // boolean to keep track if the managed plugin is loaded successfully + bool bIsPluginLoaded; + int iMajorVersion; + wchar_t* wszCLRVersion; + wchar_t* wszAppBase; + bool bIsDisposed; + + // fields needed to validate 2 plugins initializing at the same time + CRITICAL_SECTION criticalSection; + bool isCSInitSucceeded; + + // Abstraction of the differences between CLR hosting environments with + // repsect to the interface with pspluginwkr. + IPowerShellClrHost* powerShellClrHost; + + // Default no-op implementation used for the output functions. + NativeMsh::PwrshCommon pwrshCommon; + + // private constructor..to make Mediator Singleton + PwrshPlugInMediator() + { + bIsPluginLoaded = false; + bIsDisposed = false; + iMajorVersion = 0; + wszCLRVersion = NULL; + wszAppBase = NULL; + + hShutdownPluginMethodAddress = NULL; + hCreateShellMethodAddress = NULL; + hReleaseShellMethodAddress = NULL; + hCreateCommandMethodAddress = NULL; + hReleaseCommandMethodAddress = NULL; + hSendMethodAddress = NULL; + hReceiveMethodAddress = NULL; + hSignalMethodAddress = NULL; + hConnectMethodAddress = NULL; + + isCSInitSucceeded = false; + + powerShellClrHost = NULL; + + // spin count is set to 1024 + if (InitializeCriticalSectionAndSpinCount(&criticalSection, 0x80000400)) + { + isCSInitSucceeded = true; + } + } + + // Clear plugin specific resources. + void CleanUp() + { + if (!bIsDisposed) + { + bIsDisposed = true; + + if (NULL != powerShellClrHost) + { + delete powerShellClrHost; + powerShellClrHost = NULL; + } + + if (NULL != wszCLRVersion) + { + delete[] wszCLRVersion; + wszCLRVersion = NULL; + } + + if (NULL != wszAppBase) + { + delete[] wszAppBase; + wszAppBase = NULL; + } + } + } + +public: + + static PwrshPlugInMediator* GetPwrshPlugInMediator(__in_opt PCWSTR extraInfo) throw (...) + { + // to make plugin mediator singleton + // Create object, when called for the first time + static PwrshPlugInMediator singletonInstance; + + if (!singletonInstance.isCSInitSucceeded) + { + PWSTR msg = NULL; + GetFormattedErrorMessage(&msg, g_INIT_CRITICALSECTION_FAILED); + throw new PlugInException(g_INIT_CRITICALSECTION_FAILED, msg); + } + + // this check avoids entering/exiting critical sections + // frequently + if (!singletonInstance.bIsPluginLoaded) + { + EnterCriticalSection(&singletonInstance.criticalSection); + try + { + if (!singletonInstance.bIsPluginLoaded) + { + // process extra info initializies the pwrshplugin + // by initializing the CLR version and obtaining access + // pointers for the plugin worker or for System.Management.Automation.dll. + singletonInstance.ProcessExtraInfo(extraInfo, NULL); + } + } + catch(PlugInException* e) + { + LeaveCriticalSection(&singletonInstance.criticalSection); + throw e; + } + + LeaveCriticalSection(&singletonInstance.criticalSection); + } + + return &singletonInstance; + } + + ~PwrshPlugInMediator() + { + if (isCSInitSucceeded) + { + DeleteCriticalSection(&criticalSection); + } + + CleanUp(); + } + + // Clear plugin specific resources. + DWORD Shutdown(__in DWORD flags, __in DWORD reason) throw (...) + { + // this null condition should never occur.. but for server process safety ensure we + // fail safely.. + if (NULL != hShutdownPluginMethodAddress) + { + (hShutdownPluginMethodAddress)((PVOID)this); + } + + CleanUp(); + return NO_ERROR; + } + + PwrshPlugIn* CreatePwrshPlugIn(__in PCWSTR applicationIdentification, + __in_opt PCWSTR extraInfo) throw (...) + { + PWSTR initParameters = NULL; + // ProcessExtraInfo might throw... + // storing the extra info for plugin use later. + VerifyAndStoreExtraInfo(extraInfo, &initParameters); + PwrshPlugIn* result = new PwrshPlugIn(applicationIdentification, initParameters); + g_pPluginContext = result; + return result; + } + + VOID CreateShell( + __in PwrshPlugIn *plugInToUseWithCreateShell, + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in_opt WSMAN_SHELL_STARTUP_INFO *startupInfo, + __in_opt WSMAN_DATA *inboundShellInformation) + { + if ((NULL == plugInToUseWithCreateShell) || + (NULL == requestDetails) || + (NULL == startupInfo) || + (NULL == requestDetails->operationInfo)) + { + ReportError(requestDetails, g_INVALID_INPUT, L"WSManPluginShell"); + return; + } + + // this null condition should never occur.. but for server process safety ensure we + // fail safely.. + if (NULL == hCreateShellMethodAddress) + { + ReportError(requestDetails, g_MANAGED_METHOD_RESOLUTION_FAILED); + return; + } + + __try + { + PCWSTR initParameters = plugInToUseWithCreateShell->GetInitParameters(); + (hCreateShellMethodAddress)((PVOID)this, requestDetails, flags, initParameters, startupInfo, inboundShellInformation); + } + __except(ProcessException(requestDetails, GetExceptionCode())) + { + } + } + + VOID ReleaseShell(__in PVOID shellContext) + { + // this null condition should never occur.. but for server process safety ensure we + // fail safely.. + if (NULL != hReleaseShellMethodAddress) + { + (hReleaseShellMethodAddress)((PVOID)this, shellContext); + } + } + + VOID CreateCommand( + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in PVOID shellContext, + __in PCWSTR commandLine, + __in_opt WSMAN_COMMAND_ARG_SET *arguments) + { + if (NULL == requestDetails) + { + ReportError(requestDetails, g_INVALID_INPUT, L"WSManPluginCommand"); + return; + } + + if (NULL == hCreateCommandMethodAddress) + { + ReportError(requestDetails, g_MANAGED_METHOD_RESOLUTION_FAILED); + return; + } + + __try + { + (hCreateCommandMethodAddress)((PVOID)this, requestDetails, flags, shellContext, commandLine, arguments); + } + __except(ProcessException(requestDetails, GetExceptionCode())) + { + } + } + + VOID ReleaseCommand(__in PVOID shellContext, + __in PVOID commandContext) + { + // this null condition should never occur.. but for server process safety ensure we + // fail safely.. + if (NULL != hReleaseShellMethodAddress) + { + (hReleaseCommandMethodAddress)((PVOID)this, shellContext, commandContext); + } + } + + VOID ExecuteConnectToShellOrCommand( + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in PVOID shellContext, + __in_opt PVOID commandContext, + __in_opt WSMAN_DATA *inboundConnectInformation) + { + if (NULL == requestDetails) + { + ReportError(requestDetails, g_INVALID_INPUT, L"WSManPluginConnect"); + return; + } + + if (NULL == hConnectMethodAddress) + { + ReportError(requestDetails, g_MANAGED_CONNECT_METHOD_RESOLUTION_FAILED); + return; + } + + __try + { + (hConnectMethodAddress)((PVOID)this, requestDetails, flags, shellContext, commandContext, inboundConnectInformation); + } + __except(ProcessException(requestDetails, GetExceptionCode())) + { + } + + } + + VOID SendOneItemToShellOrCommand( + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in PVOID shellContext, + __in_opt PVOID commandContext, + __in PCWSTR stream, + __in WSMAN_DATA *inboundData) + { + if (NULL == requestDetails) + { + ReportError(requestDetails, g_INVALID_INPUT, L"WSManPluginSend"); + return; + } + + if (NULL == hSendMethodAddress) + { + ReportError(requestDetails, g_MANAGED_METHOD_RESOLUTION_FAILED); + return; + } + + __try + { + (hSendMethodAddress)((PVOID)this, requestDetails, flags, shellContext, commandContext, stream, inboundData); + } + __except(ProcessException(requestDetails, GetExceptionCode())) + { + } + } + + VOID EnableShellOrCommandToSendDataToClient( + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in PVOID shellContext, + __in_opt PVOID commandContext, + __in_opt WSMAN_STREAM_ID_SET* streamSet) + { + if (NULL == requestDetails) + { + ReportError(requestDetails, g_INVALID_INPUT, L"WSManPluginReceive"); + return; + } + + if (NULL == hReceiveMethodAddress) + { + ReportError(requestDetails, g_MANAGED_METHOD_RESOLUTION_FAILED); + return; + } + + __try + { + (hReceiveMethodAddress)((PVOID)this, requestDetails, flags, shellContext, commandContext, streamSet); + } + __except(ProcessException(requestDetails, GetExceptionCode())) + { + } + } + + VOID SignalShellOrCmd( + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in PVOID shellContext, + __in_opt PVOID commandContext, + __in PCWSTR code) + { + if (NULL == requestDetails) + { + ReportError(requestDetails, g_INVALID_INPUT, L"WSManPluginSignal"); + return; + } + + if (NULL == hSignalMethodAddress) + { + ReportError(requestDetails, g_MANAGED_METHOD_RESOLUTION_FAILED); + return; + } + + __try + { + (hSignalMethodAddress)((PVOID)this, requestDetails, flags, shellContext, commandContext, code); + } + __except(ProcessException(requestDetails, GetExceptionCode())) + { + } + } + +private: + // checks if the current plugin is active. + inline bool IsActive() + { + return bIsDisposed; + } + + /* Process error code from the exception. The current code always + * returns EXCEPTION_CONTINUE_SEARCH after logging the error code. + * + * requestDetails is used to report WSMan operation complete + */ + int ProcessException(WSMAN_PLUGIN_REQUEST *requestDetails, unsigned int errorCode) + { + WSManPluginOperationComplete(requestDetails, 0, errorCode, NULL); + return EXCEPTION_CONTINUE_EXECUTION; + } + + DWORD ReportError(WSMAN_PLUGIN_REQUEST *requestDetails, DWORD dwMessageId, ...) + { + PWSTR extendedErrorInformation = NULL; + + va_list args; + va_start(args, dwMessageId); + + GetFormattedErrorMessage(&extendedErrorInformation, dwMessageId, &args); + + va_end(args); + + DWORD errorCode = dwMessageId; + DWORD result = WSManPluginOperationComplete(requestDetails, 0, errorCode, extendedErrorInformation); + + if (NULL != extendedErrorInformation) + { + delete[] extendedErrorInformation; + } + + return result; + } + + DWORD ReportError(WSMAN_PLUGIN_REQUEST *requestDetails, PlugInException* e) + { + DWORD errorCode = e->dwMessageId; + + DWORD result = WSManPluginOperationComplete(requestDetails, 0, errorCode, e->extendedErrorInformation); + return result; + } + + unsigned int CreateMgdPluginFileName( + int iVPSMajorVersion, + int iVPSMinorVersion, + _In_ PWSTR wszVAppBase, + __out PWSTR* wszMgdPlugInFileName) + { + int monadMajorVersion = iVPSMajorVersion; + int monadMinorVersion = iVPSMinorVersion; + wchar_t * wszMonadVersion = NULL; + wchar_t* wszTempCLRVersion = NULL; + unsigned int exitCode = EXIT_CODE_SUCCESS; + + do + { + iMajorVersion = iVPSMajorVersion; + + exitCode = ConstructPowerShellVersion(iVPSMajorVersion, iVPSMinorVersion, &wszMonadVersion); + if (EXIT_CODE_SUCCESS != exitCode) + { + break; + } + + // Read managed plugin's full path from registry, if such a path exists. + exitCode = pwrshCommon.GetRegistryInfo( + &wszMonadVersion, + &monadMajorVersion, + monadMinorVersion, + &wszTempCLRVersion, + g_PSPLUGINWKRV3_REGISTRY_KEY, + wszMgdPlugInFileName); + + if (*wszMgdPlugInFileName == NULL) + { + // Reset exit code to EXIT_CODE_SUCCESS + exitCode = EXIT_CODE_SUCCESS; + + // construct managed plugin's full path + size_t iAppBaseLength; + size_t iFileNameLength; + size_t iTotalLength; + + if (FAILED(StringCchLength(wszVAppBase, STRSAFE_MAX_CCH, &iAppBaseLength)) || + FAILED(StringCchLength(g_MANAGED_PLUGIN_FILENAME_STRING, STRSAFE_MAX_CCH, &iFileNameLength))) + { + exitCode = g_INVALID_INPUT; + break; + } + + iTotalLength = iAppBaseLength + iFileNameLength + 2; + + *wszMgdPlugInFileName = new wchar_t[iTotalLength]; + if (NULL == *wszMgdPlugInFileName) + { + exitCode = E_OUTOFMEMORY; + break; + } + (*wszMgdPlugInFileName)[0] = '\0'; + + if (FAILED(StringCchCopyW(*wszMgdPlugInFileName, iTotalLength, wszVAppBase)) || + FAILED(StringCchCatW(*wszMgdPlugInFileName, iTotalLength, L"\\")) || + FAILED(StringCchCatW(*wszMgdPlugInFileName, iTotalLength, g_MANAGED_PLUGIN_FILENAME_STRING))) + { + exitCode = g_MANAGED_PLUGIN_PATH_CONSTRUCTION_ERROR; + break; + } + } + } while (false); + + if (NULL != wszMonadVersion) + { + delete[] wszMonadVersion; + } + + if (NULL != wszTempCLRVersion) + { + delete[] wszTempCLRVersion; + } + return exitCode; + } + + // returns non-zero code on error + plugin exception is populated in some cases + // like plugin load error. so the caller is expected to check both these + // to see if there is any error. + unsigned int LoadManagedPlugIn( + _In_ PWSTR wszMgdPlugInFileName, + _In_ PWSTR wszVCLRVersion, // Conditionally set to wszCLRVersion on success + _In_ PWSTR wszVAppBase, // Conditionally set to wszAppBase on success + __out_opt PlugInException** pPluginException ) + { + unsigned int exitCode = EXIT_CODE_SUCCESS; + + if (bIsPluginLoaded) + { + return g_MANAGED_PLUGIN_ALREADY_LOADED; + } + + if ((NULL == wszVCLRVersion) || (NULL == wszVAppBase)) + { + return g_INVALID_INPUT; + } + + do + { + *pPluginException = NULL; + + // Setting global AppBase and CLR Version + wszCLRVersion = wszVCLRVersion; + wszAppBase = wszVAppBase; + + // Load managed plugin worker.. + + PwrshPluginWkr_Ptrs workerCallbackPtrs; + memset(&workerCallbackPtrs, 0, sizeof(PwrshPluginWkr_Ptrs)); + + // The loader will obtain the worker pointers from the appropriate DLL. + // If this call succeeds, PwrshPluginWkr_Ptrs should be populated. + exitCode = powerShellClrHost->LoadWorkerCallbackPtrs(&workerCallbackPtrs, wszMgdPlugInFileName, pPluginException); + + if (EXIT_CODE_SUCCESS == exitCode) + { + // Now get the proc address for all the plugin API.. + hShutdownPluginMethodAddress = workerCallbackPtrs.shutdownPluginFuncPtr; + hCreateShellMethodAddress = workerCallbackPtrs.wsManPluginShellFuncPtr; + hReleaseShellMethodAddress = workerCallbackPtrs.wsManPluginReleaseShellContextFuncPtr; + hCreateCommandMethodAddress = workerCallbackPtrs.wsManPluginCommandFuncPtr; + hReleaseCommandMethodAddress = workerCallbackPtrs.wsManPluginReleaseCommandContextFuncPtr; + hSendMethodAddress = workerCallbackPtrs.wsManPluginSendFuncPtr; + hReceiveMethodAddress = workerCallbackPtrs.wsManPluginReceiveFuncPtr; + hSignalMethodAddress = workerCallbackPtrs.wsManPluginSignalFuncPtr; + hConnectMethodAddress = workerCallbackPtrs.wsManPluginConnectFuncPtr; + } + + if (// if pwrsplugin v2 plugin, this function does not exist + // (NULL == hShutdownPluginMethodAddress) || + (NULL == hCreateShellMethodAddress) || (NULL == hReleaseShellMethodAddress) || + (NULL == hCreateCommandMethodAddress) || (NULL == hReleaseCommandMethodAddress) || + (NULL == hSendMethodAddress) || (NULL == hReceiveMethodAddress) || + (NULL == hSignalMethodAddress)) + { + PWSTR msg = NULL; + exitCode = g_MANAGED_METHOD_RESOLUTION_FAILED; + GetFormattedErrorMessage(&msg, g_MANAGED_METHOD_RESOLUTION_FAILED); + *pPluginException = new PlugInException(exitCode, msg); + break; + } + + bIsPluginLoaded = true; + }while(false); + + if (exitCode != EXIT_CODE_SUCCESS) + { + wszCLRVersion = NULL; + wszAppBase = NULL; + } + + return exitCode; + } + + void LoadPowerShell(PCWSTR version) throw (...) + { + // Verify incoming powershell version format. + int iPSMajorVersion = 0; + int iPSMinorVersion = 0; + if (!pwrshCommon.VerifyMonadVersionFormat(version, &iPSMajorVersion, &iPSMinorVersion, true, false)) + { + PWSTR msg = NULL; + GetFormattedErrorMessage(&msg, g_OPTION_SET_NOT_COMPLY, g_BUILD_VERSION); + throw new PlugInException(g_OPTION_SET_NOT_COMPLY, msg); + } + + // client is requesting powershell 1.x version. Remoting doesn't support + // powershell 1.0, remoting is supported from 2.0 + if (1 >= iPSMajorVersion) + { + PWSTR msg = NULL; + GetFormattedErrorMessage(&msg, g_OPTION_SET_NOT_COMPLY, g_BUILD_VERSION); + throw new PlugInException(g_OPTION_SET_NOT_COMPLY, msg); + } + + // the reg key from 2 and 1 is 1.. + int requestedMonadMajorVersion = iPSMajorVersion; + if (2 == requestedMonadMajorVersion) + { + requestedMonadMajorVersion = 1; + } + + wchar_t* wszMonadVersion = NULL; // Allocated via ConstructPowerShellVersion || GetRegistryInfo + wchar_t* wszTempCLRVersion = NULL; // Allocated via GetRegistryInfo + wchar_t* wszTempAppBase = NULL; // Allocated via GetRegistryInfo + PWSTR wszMgdPlugInFileName = NULL; // Allocted in CreateMgdPluginFileName + unsigned int exitCode = EXIT_CODE_SUCCESS; + PlugInException* pErrorMsg = NULL; + + do + { + exitCode = ConstructPowerShellVersion(iPSMajorVersion, iPSMinorVersion, &wszMonadVersion); + if (exitCode != EXIT_CODE_SUCCESS) + { + PWSTR msg = NULL; + GetFormattedErrorMessage(&msg, g_OPTION_SET_NOT_COMPLY, g_BUILD_VERSION); + pErrorMsg = new PlugInException(exitCode, msg); + break; + } + + // Read CLR version from registry + exitCode = pwrshCommon.GetRegistryInfo( + &wszMonadVersion, + &requestedMonadMajorVersion, + iPSMinorVersion, + &wszTempCLRVersion, + L"ApplicationBase", + &wszTempAppBase); + if (EXIT_CODE_SUCCESS != exitCode) + { + PWSTR msg = NULL; + GetFormattedErrorMessage(&msg, g_OPTION_SET_NOT_COMPLY, g_BUILD_VERSION); + pErrorMsg = new PlugInException(exitCode, msg); + break; + } + + exitCode = CreateMgdPluginFileName(requestedMonadMajorVersion, iPSMinorVersion, wszTempAppBase, &wszMgdPlugInFileName); + if (EXIT_CODE_SUCCESS != exitCode) + { + break; + } + + if (!bIsPluginLoaded) + { + this->powerShellClrHost = PowerShellClrWorkerFactory(wszMgdPlugInFileName); + if (NULL == this->powerShellClrHost) + { + exitCode = ERROR_NOT_ENOUGH_MEMORY; + break; + } + + exitCode = powerShellClrHost->LaunchClr(wszMonadVersion, wszTempCLRVersion); + if (EXIT_CODE_SUCCESS != exitCode) + { + PWSTR msg = NULL; + GetFormattedErrorMessage(&msg, g_CLR_LOAD_FAILED, wszTempCLRVersion); + pErrorMsg = new PlugInException(g_CLR_LOAD_FAILED, msg); + break; + } + + exitCode = LoadManagedPlugIn(wszMgdPlugInFileName, wszTempCLRVersion, wszTempAppBase, &pErrorMsg); + } + else + { + if (requestedMonadMajorVersion != iMajorVersion) + { + exitCode = g_OPTION_SET_MAJOR_VERSION_NOT_MATCH; + } + else if (0 != _wcsnicmp(wszTempCLRVersion, wszCLRVersion, 20)) + { + exitCode = g_OPTION_SET_CLR_VERSION_NOT_MATCH; + } + else if (0 != _wcsicmp(wszTempAppBase, wszAppBase)) + { + exitCode = g_OPTION_SET_APP_BASE_NOT_MATCH; + } + } + } while (false); + + if (NULL != wszMonadVersion) + { + delete[] wszMonadVersion; + } + + if (NULL != wszMgdPlugInFileName) + { + delete[] wszMgdPlugInFileName; + } + + if (exitCode != EXIT_CODE_SUCCESS) + { + // Assigned to this->wszCLRVersion on success in LoadManagedPlugIn, so it shouldn't be freed here on success + if (NULL != wszTempCLRVersion) + { + delete[] wszTempCLRVersion; + } + + // Assigned to this->wszAppBase on success LoadManagedPlugIn, so it shouldn't be freed here on success + if (NULL != wszTempAppBase) + { + delete[] wszTempAppBase; + } + + if (NULL != pErrorMsg) + { + throw pErrorMsg; + } + else + { + PWSTR msg = NULL; + GetFormattedErrorMessage(&msg, g_OPTION_SET_NOT_COMPLY, g_BUILD_VERSION); + throw new PlugInException(exitCode, msg); + } + } + } + +public: + // extraInfo is supplied by WSMan and WSMan validates the XML syntax + // before supplying this value to plugin..because of this, the following + // method will not check for xml element syntax. + void VerifyAndStoreExtraInfo(PCWSTR extraInfo, + __deref_opt_out PWSTR *initParameters) throw(...) + { + if (NULL == extraInfo) + { + PWSTR msg = NULL; + GetFormattedErrorMessage(&msg, + g_PSVERSION_NOT_FOUND_IN_CONFIG, g_PSVERSION_CONFIG, g_INITIALIZATIONPARAM_CONFIG); + throw new PlugInException(g_PSVERSION_NOT_FOUND_IN_CONFIG, msg); + } + + size_t initParamsLength; + if (FAILED(StringCchLength(extraInfo, STRSAFE_MAX_CCH, &initParamsLength))) + { + PWSTR msg = NULL; + GetFormattedErrorMessage(&msg, + g_BAD_INITPARAMETERS, g_INITIALIZATIONPARAM_CONFIG); + throw new PlugInException(g_BAD_INITPARAMETERS, msg); + } + + if (NULL != initParameters) + { + // make a local copy of the extra info for future use + // this will be used whenever a new shell is created. + *initParameters = new wchar_t[initParamsLength + 1]; + if (FAILED(StringCchCopyNW(*initParameters, initParamsLength + 1, extraInfo, initParamsLength))) + { + PWSTR msg = NULL; + GetFormattedErrorMessage(&msg, + g_BAD_INITPARAMETERS, g_INITIALIZATIONPARAM_CONFIG); + throw new PlugInException(g_BAD_INITPARAMETERS, msg); + } + } + } + + void ProcessExtraInfo(PCWSTR extraInfo, + __deref_opt_out PWSTR *initParameters) throw(...) + { + VerifyAndStoreExtraInfo(extraInfo, initParameters); + + // Get PSVersion and MaxPSVersion values from the config xml + wchar_t* psversion = NULL; + wchar_t* maxpsversion = NULL; + // Win8: 97936: To support backward compatability, if PSVersion = 2.0 and AssemblyToken + // is specified we set maxPSVersion = 2.0..so that the endpoint is not automatically + // transferred to PS 3.0 + wchar_t* assemblyToken = NULL; + + // This will hold the powershell version calculated from psversion and maxpsversion + wchar_t* version = NULL; + + size_t initParamsLength; + if (FAILED(StringCchLength(extraInfo, STRSAFE_MAX_CCH, &initParamsLength))) + { + PWSTR msg = NULL; + GetFormattedErrorMessage(&msg, + g_BAD_INITPARAMETERS, g_INITIALIZATIONPARAM_CONFIG); + throw new PlugInException(g_BAD_INITPARAMETERS, msg); + } + + PCWSTR param = wcsstr(extraInfo, L" 0) + { + version = new wchar_t[length + 1]; + + try + { + if (FAILED(StringCchCopyNW(version, length + 1, startVersion, length))) + { + PWSTR msg = NULL; + GetFormattedErrorMessage(&msg, + g_BAD_INITPARAMETERS, g_INITIALIZATIONPARAM_CONFIG); + throw new PlugInException(g_BAD_INITPARAMETERS, msg); + } + return version; + } + catch(...) + { + if (NULL != version) + { + delete[] version; + } + throw; + } + + // delete the version in non-throw case. + if (NULL != version) + { + delete[] version; + } + } + } + return NULL; + } + + wchar_t* CalculatePowershellVersion(__in wchar_t* psversion, + __in_opt wchar_t* maxpsversion) + { + wchar_t* version = NULL; + int majorPSVersionNumber = -1, minorPSVersionNumber = -1; + int majorMaxPSVersionNumber = -1, minorMaxPSVersionNumber = -1 ; + if (pwrshCommon.VerifyMonadVersionFormat(psversion, &majorPSVersionNumber, &minorPSVersionNumber, true, true)) + { + if (maxpsversion != NULL) + { + if (pwrshCommon.VerifyMonadVersionFormat(maxpsversion, &majorMaxPSVersionNumber, &minorMaxPSVersionNumber, true, true)) + { + // TODO: Don't understand this. + if (majorMaxPSVersionNumber > majorPSVersionNumber) + { + PWSTR msg = NULL; + GetFormattedErrorMessage(&msg, + g_BAD_INITPARAMETERS, g_INITIALIZATIONPARAM_CONFIG); + throw new PlugInException(g_BAD_INITPARAMETERS, msg); + } + + // TODO: why hardcoding is needed here + if (majorPSVersionNumber == 3 && majorMaxPSVersionNumber == 2) + { + PWSTR msg = NULL; + GetFormattedErrorMessage(&msg, + g_BAD_INITPARAMETERS, g_INITIALIZATIONPARAM_CONFIG); + throw new PlugInException(g_BAD_INITPARAMETERS, msg); + } + + if (majorPSVersionNumber == 2 && majorMaxPSVersionNumber == 2) + { + size_t length; + if (FAILED(StringCchLength(psversion, STRSAFE_MAX_CCH, &length))) + { + PWSTR msg = NULL; + GetFormattedErrorMessage(&msg, + g_BAD_INITPARAMETERS, g_INITIALIZATIONPARAM_CONFIG); + throw new PlugInException(g_BAD_INITPARAMETERS, msg); + } + + version = new wchar_t[length + 1]; + // Set version as the psversion + if (FAILED(StringCchCopyNW(version, length + 1, psversion, length))) + { + PWSTR msg = NULL; + GetFormattedErrorMessage(&msg, + g_BAD_INITPARAMETERS, g_INITIALIZATIONPARAM_CONFIG); + throw new PlugInException(g_BAD_INITPARAMETERS, msg); + } + } + } + } + else + { + version = L"3.0"; + } + } + return version; + } +}; + +void WINAPI PerformWSManPluginReportCompletion() +{ + // Now report the plugin completion, to indicate that plugin is ready to shutdown. + // This API is used by plugins to report completion + // - pluginContext MUST be the same context that plugin provided to the WSManPluginStartup method + // - flags are reserved, so 0 + WSManPluginReportCompletion(g_pPluginContext, 0); +} \ No newline at end of file diff --git a/src/powershell-native/nativemsh/pwrshplugin/pwrshpluginResources.rc b/src/powershell-native/nativemsh/pwrshplugin/pwrshpluginResources.rc new file mode 100644 index 000000000..864a873e3 --- /dev/null +++ b/src/powershell-native/nativemsh/pwrshplugin/pwrshpluginResources.rc @@ -0,0 +1,13 @@ +// Copyright (C) 2005 Microsoft Corporation +// +// resource script for msh.exe +// + +#include + +#define MANIFEST_RESOURCE_ID 1 + +MANIFEST_RESOURCE_ID RT_MANIFEST "pwrshplugin.dll.manifest" + +#include "pwrshpluginerrorcodes.rc" +#include "version.rc" \ No newline at end of file diff --git a/src/powershell-native/nativemsh/pwrshplugin/pwrshplugindefs.h b/src/powershell-native/nativemsh/pwrshplugin/pwrshplugindefs.h new file mode 100644 index 000000000..b34f6a6ee --- /dev/null +++ b/src/powershell-native/nativemsh/pwrshplugin/pwrshplugindefs.h @@ -0,0 +1,159 @@ +// ---------------------------------------------------------------------- +// +// Microsoft Windows NT +// Copyright (C) Microsoft Corporation, 2007. +// +// Contents: Headers used by pwrshplugin. +// pwrshplugin is totally unmanaged. +// ---------------------------------------------------------------------- + +#pragma once + +#include +#include + +// include wsman.h header file..it is required to declare the version +// number of the wsman API to use +#define WSMAN_API_VERSION_1_0 +#include + +// plugin errors start from 1000 +const int g_NULL_PLUGIN_CONTEXT = 1100; +const int g_CREATION_FAILED = 1101; +const int g_MANAGED_PLUGIN_ALREADY_LOADED = 1102; +const int g_MANAGED_PLUGIN_PATH_CONSTRUCTION_ERROR = 1103; +const int g_MANAGED_PLUGIN_LOAD_FAILED = 1104; + +const int g_SHELL_CREATION_FAILED = 1201; +const int g_OPTION_SET_MAJOR_VERSION_NOT_MATCH = 1202; +const int g_OPTION_SET_CLR_VERSION_NOT_MATCH = 1203; +const int g_OPTION_SET_APP_BASE_NOT_MATCH = 1204; + +const PCWSTR g_VERSION_OPTION_STRING = L"version"; +const PCWSTR g_PSPLUGINWKRV3_REGISTRY_KEY = L"PSPluginWkrModuleName"; +const PCWSTR g_MANAGED_PLUGIN_FILENAME_STRING = L"pspluginwkr.dll"; +const PCWSTR g_MANAGED_PLUGIN_FILENAME_V3_STRING = L"system.management.automation.dll"; +const PCWSTR g_PSVERSION_CONFIG = L"PSVersion"; +const PCWSTR g_INITIALIZATIONPARAM_CONFIG = L"InitializationParameters"; + +// The following VER_ macros are defined in ntverp.h +const PCWSTR g_BUILD_VERSION = LVER_PRODUCTVERSION_STR; + +// Managed and mixed-mode plugin worker method entrypoints +// +// The v3 version of the worker is a mixed-mode managed C++ DLL, so it can +// be accessed directly using GetProcAddress. +// The CoreCLR-compliant worker is included in System.Management.Automation.dll, +// so we have to use alternate means to acquire the function pointers. +// Once we have the pointers, they may be used in the same way regardless of the +// method used to obtain them. + +typedef void (WINAPI *ShutdownPluginFuncPtr)(__in PVOID pluginContext); + +typedef void (WINAPI *WSManPluginShellFuncPtr)( + __in PVOID pluginContext, + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in PCWSTR extraInfo, + __in_opt WSMAN_SHELL_STARTUP_INFO *startupInfo, + __in_opt WSMAN_DATA *inboundShellInformation + ); + +typedef void (WINAPI *WSManPluginConnectFuncPtr)( + __in PVOID pluginContext, + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in PVOID shellContext, + __in_opt PVOID commandContext, + __in_opt WSMAN_DATA *inboundConnectInformation + ); + +typedef void (WINAPI *WSManPluginReleaseShellContextFuncPtr)( + __in PVOID pluginContext, + __in PVOID shellContext + ); + +typedef void (WINAPI *WSManPluginCommandFuncPtr)( + __in PVOID pluginContext, + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in PVOID shellContext, + __in PCWSTR commandLine, + __in_opt WSMAN_COMMAND_ARG_SET *arguments + ); + +typedef void (WINAPI *WSManPluginReleaseCommandContextFuncPtr)( + __in PVOID pluginContext, + __in PVOID shellContext, + __in PVOID commandContext + ); + +typedef void (WINAPI *WSManPluginSendFuncPtr)( + __in PVOID pluginContext, + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in PVOID shellContext, + __in_opt PVOID commandContext, + __in PCWSTR stream, + __in WSMAN_DATA *inboundData + ); + +typedef void (WINAPI *WSManPluginReceiveFuncPtr)( + __in PVOID pluginContext, + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in PVOID shellContext, + __in_opt PVOID commandContext, + __in_opt WSMAN_STREAM_ID_SET* streamSet + ); + +typedef void (WINAPI *WSManPluginSignalFuncPtr)( + __in PVOID pluginContext, + __in WSMAN_PLUGIN_REQUEST *requestDetails, + __in DWORD flags, + __in PVOID shellContext, + __in_opt PVOID commandContext, + __in PCWSTR code); + +typedef struct _PwrshPluginWkr_Ptrs +{ + ShutdownPluginFuncPtr shutdownPluginFuncPtr; + WSManPluginShellFuncPtr wsManPluginShellFuncPtr; + WSManPluginReleaseShellContextFuncPtr wsManPluginReleaseShellContextFuncPtr; + WSManPluginCommandFuncPtr wsManPluginCommandFuncPtr; + WSManPluginReleaseCommandContextFuncPtr wsManPluginReleaseCommandContextFuncPtr; + WSManPluginSendFuncPtr wsManPluginSendFuncPtr; + WSManPluginReceiveFuncPtr wsManPluginReceiveFuncPtr; + WSManPluginSignalFuncPtr wsManPluginSignalFuncPtr; + WSManPluginConnectFuncPtr wsManPluginConnectFuncPtr; +} PwrshPluginWkr_Ptrs; + +class PlugInException +{ +public: + DWORD dwMessageId; + // This message is allocated outside the exception, but the exception frees + // the memory. + PWSTR extendedErrorInformation; + + PlugInException(DWORD msgId, __in PWSTR msg) + { + dwMessageId = msgId; + extendedErrorInformation = msg; + } + + ~PlugInException() + { + if (NULL != extendedErrorInformation) + { + delete[] extendedErrorInformation; + extendedErrorInformation = NULL; + } + } +private: + // Provided without implementation to prevent automatic instantiation and copying + // since copying will lead to double frees given the existing code. + PlugInException(); + PlugInException(const PlugInException&); + PlugInException& operator=(const PlugInException&); +}; diff --git a/src/powershell-native/nativemsh/pwrshplugin/pwrshpluginerrorcodes.mc b/src/powershell-native/nativemsh/pwrshplugin/pwrshpluginerrorcodes.mc new file mode 100644 index 000000000..30feceb47 --- /dev/null +++ b/src/powershell-native/nativemsh/pwrshplugin/pwrshpluginerrorcodes.mc @@ -0,0 +1,80 @@ +;// Copyright (C) Microsoft Corporation, 2000 - 2007 + +;#undef FACILITY_POWERSHELL +FacilityNames=(PowerShell=84:FACILITY_POWERSHELL) +SeverityNames=(Success=0x0 + CoError=0x2 + ) + +MessageId=1001 +Facility=POWERSHELL +Severity=CoError +SymbolicName=g_INVALID_PLUGIN_CONTEXT +Language=English +The supplied plugin context is invalid. +. + +MessageId=1002 +Facility=POWERSHELL +Severity=CoError +SymbolicName=g_INVALID_INPUT +Language=English +Invalid parameter to plugin method %1!ls!. +. + +MessageId=1003 +Facility=POWERSHELL +Severity=CoError +SymbolicName=g_MANAGED_METHOD_RESOLUTION_FAILED +Language=English +Unable to find plugin methods WSManPluginShell, WSManPluginCommand, WSManPluginSend, WSManPluginRecieve in the Managed plugin module. +. + +MessageId=1004 +Facility=POWERSHELL +Severity=CoError +SymbolicName=g_OPTION_SET_NOT_COMPLY +Language=English +Powershell plugin does not support the options requested. Make sure client is compatible with build %1!ls! of PowerShell. +. + +MessageId=1005 +Facility=POWERSHELL +Severity=CoError +SymbolicName=g_PSVERSION_NOT_FOUND_IN_CONFIG +Language=English +A "%1!ls!" name-value pair is expected in the "%2!ls!" element of the configuration xml. Contact your administrator. +. + +MessageId=1006 +Facility=POWERSHELL +Severity=CoError +SymbolicName=g_BAD_INITPARAMETERS +Language=English +The "%1!ls!" element in the configuration xml is badly formatted. Contact your administrator. +. + +MessageId=1007 +Facility=POWERSHELL +Severity=CoError +SymbolicName=g_INIT_CRITICALSECTION_FAILED +Language=English +An error occurred while initializing the plugin. Contact your administrator. +. + +MessageId=1008 +Facility=POWERSHELL +Severity=CoError +SymbolicName=g_CLR_LOAD_FAILED +Language=English +An error occurred while loading version %1!ls! of CLR. Contact your administrator. +. + +MessageId=1009 +Facility=POWERSHELL +Severity=CoError +SymbolicName=g_MANAGED_CONNECT_METHOD_RESOLUTION_FAILED +Language=English +An error occured while processing the connect operation. A relevant entry point is not found in the managed plugin module +Make sure that the configured plugin supports connect operation. +. diff --git a/src/powershell-native/nativemsh/pwrshplugin/version.rc b/src/powershell-native/nativemsh/pwrshplugin/version.rc new file mode 100644 index 000000000..20f390fdd --- /dev/null +++ b/src/powershell-native/nativemsh/pwrshplugin/version.rc @@ -0,0 +1,13 @@ +// +// Copyright (C) Microsoft. All rights reserved. +// +#include +#include + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "pwrshplugin.dll" +#define VER_INTERNALNAME_STR "pwrshplugin.dll" +#define VER_ORIGINALFILENAME_STR "pwrshplugin.dll" + +#include